Is loosing TextField text due to app focus out an expected behaviour? - flutter

I've created the simplest app: it's just a default project with added TextController and TextField. Nothing more. Here is a code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
final TextEditingController _controller = new TextEditingController();
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(controller: _controller,),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
The actions I'm doing: tap on the text field, enter several symbols from the onscreen keyboard without committing, then press the switch app button. After that, I see the list of running apps with the current flutter app with the entered text. Then I click on the flutter app and entered text disappears.
I'm expecting that it's wrong behaviour. The text should not disappear.
Am I wrong? Or I have missed something? I'm new to flutter and everything here is very strange and it conflicts with practically all my previous experience.

yes it's expected because when you enter your widget it's state gets build, and you declared the variable in state class, so every time the state is build the _controller will be assigned to a new instance of TextEditingController and the previous instance of TextEditingController(which had your value inside of it) will be available for garbage collection.

Related

How to interact with a child Stateful Widget from a parent Stateless Widget?

Context :
Say I have a custom DisplayElapsedTime Widget which is Stateful and holds a Text Widget. With a Timer, every 1sec, the text value is updated with the current elapsed time given by a Stopwatch.
And now, say I have a page which is Stateless and has the DisplayElapsedTime Widget as a child.
What I would like to do :
On the click of a "Start" button in my page, I would like to start the DisplayElapsedTime (which means starting the Stopwatch and the Timer).
From my page, I would also like to have access to the elapsed time value of the Stopwatch "whenever I want".
Why I am having a hard time :
So far (see: I use Stateless Widget alongside BLoC almost everytime. Am I wrong?), I have almost always worked with Stateless Widget alongside the pattern BLoC and never used Stateful. Currently, having extremely long and complex Widgets, I am starting to sense the "limits" of not using the better of the two worlds. But I don't quite fully understand how the Widgets should be interacting between one another.
I really cannot find the solution to my problem anywhere (or am really bad at searching). However, surely, I cannot be the first person to want to have "control" over a Stateful Widget from a Stateless Widget, right ?
Thank you so much in advance for any help.
If I understand your question correctly, let me try to explain this using the most familiar app of all time, the beginning counter app.
This snippet contains a single StatefulWidget that controls its ability to rebuild using its setState method _incrementCounter. So, the value is incremented and the widget is rebuilt whenever the StatefulWidget calls the setState method inside itself.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
A StatefulWidget can fully rebuild itself, and when doing so, it may also rebuild all its children downstream of it in the widget tree (but not always, as const widgets are not rebuilt). To get another widget to rebuild a parent widget (upstream of it in the widget tree), you need to have that StatefulWidget's setState function. This can be done using a callback function. A callback function is made by the parent widget and passed to a child widget. So, in the following example, I have made a StatelessWidget with a button, which controls its parent widget because it calls its parent's callback function; notice that I give:
ExampleStlessWidget(counter: _counter, fx: _incrementCounter),
and not:
ExampleStlessWidget(counter: _counter, fx: _incrementCounter()),
Passing _incrementCounter() with the parenthesis calls it at the moment it is passed, while _incrementCounter allows it to be called downstream in the widget tree.
Use the callback function in the child widget by calling it anywhere (notice the parentheses).
onPressed: () {
fx();
},
Here is the new code
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ExampleStlessWidget(counter: _counter, fx: _incrementCounter),
);
}
}
class ExampleStlessWidget extends StatelessWidget {
const ExampleStlessWidget({
super.key,
required int counter,
required this.fx,
}) : _counter = counter;
final int _counter;
final Function fx;
#override
Widget build(BuildContext context) {
return Column(
children: [
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
ElevatedButton(
onPressed: () {
fx();
},
child: const Text('Click me'),
),
],
);
}
}
A bloc involves an inherited widget, which allows for monitoring the state throughout the widget tree and rebuilds widgets depending on that state. So, a bloc doesn't need a StatefulWidget to change UI. It would help if you did not look at one tool's ability to rebuild widgets as bad or good. It would be best to look at StatefulWidgets and BLoC as different tools for different jobs.
I hope this helps. Happy coding.

Creating String by using onPressed function

I want to make a User's settings screen with a couple of FlatButtons. I want to use onPressed function to create String when the button is pressed and than pass it further to Settings object and save it on disk.
The problem is - I'm new to Dart/Flutter and completely out of ideas here. I have read the docs about Shared Preferences, Streams, Async etc., but I was unable to find an answer to:
How can I create a String by using onPressed?
Can someone tell me how the code would look like or is it even the right approach to solve it this way.
Thanks in advance!
Alright , you can do it in multiple ways.
Here i created an example for you copy/paste run it so you can understand .
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
List<String> settings = new List();
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
(settings.isEmpty) ?Center(child:Text("no settings found")) :
ListView.builder(shrinkWrap: true,itemCount: settings.length,itemBuilder: (context,i)
{
return Center(child: Text("${settings[i]}"));
})
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: ()
{
setState(() {
settings.add('random setting');
});
},
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
explanation :
i created List settings wich has no data at the beginning.
and i check if setting are empty i show empty settings , else it will render the data.
the button with + sign is adding new strings to the list and will rebuild the widget to show it . with setState({});
hope that helps.

Flutter - Pass state change function to child widget and update state

I am new to flutter and building a sample app to learn it. In the above screenshot, I have created multiple widgets. My main widget contains the following widget.
Boy Girl Selector
Common Card
CounterButton (Plus or Minus)
Calculate Button
My main widget has two counter - age & weight.
CommonCard has below property :
incrementFunction() : I am setting this value from MainWidget as below.
decrementFunction()
ageIncrement() {
setState(() {
age++;
});
}
ageDecrement() {
setState(() {
age--;
});
}
value : age declared in main widget is passed to this value.
CounterButton has below property.
onPressed: increment or decrement function from parent widget is passed here through card widget.
If I keep whole code in main widget then it is working properly. But if I create multiple widget and pass increment and decrement function as argument in child widget onPressed on plus and minus is not working propely. Please share your thoughts. I am missing some fundamental of communication between child and parent widget.
There are different ways to achieve what you like as there are a couple of different state management techniques such as Dependency Injection, ChangeNotifier, BLoC, and so on (search for Flutter State Management for more details).
Here's an example of how you can achieve this on the famous counter example. This example is using dependency injection (we are passing the increment function to the a child widget as callback function). You can copy the code and past it on DartPad to quickly test it and see how it works:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
SizedBox(height: 50),
MySecondButton(secondButtonIncrement: incrementCounter),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class MySecondButton extends StatelessWidget {
MySecondButton({Key key, this.secondButtonIncrement}) : super(key: key);
final VoidCallback secondButtonIncrement;
#override
Widget build(BuildContext context) {
return FlatButton(
child: Text("Second Button"),
onPressed: () {
secondButtonIncrement();
},
color: Colors.blue);
}
}
I hope that helps.

TextEditingController doesnot change text of TextEdit at Web. May be other solution for EditText?

When the user edit text at TextEdit and then put new text by TextEditController, then the previous text was appear. Its about TextField and TextFormField. Its appear not at all tim but I cannt find out reason of it.
Its cann be uset at that time.
But may be its have a any simple other solution? I need only workable text edit field.
May be other widget? Any suggestion will be accepted with appreciate.
It will be solved, iit have a post at github:
https://github.com/flutter/flutter/issues/41376
but may be it have a simple solution/any replacement for use it now?
flutter --version is:
Flutter 1.10.7-pre.23 • channel master • https://github.com/flutter/flutter.git
Framework • revision e0247442d0 (25 hours ago) • 2019-09-26 02:49:41 -0400
Engine • revision 3713ecf107
Tools • Dart 2.6.0 (build 2.6.0-dev.2.0 c6a14f4459)
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
TextEditingController myTextController = TextEditingController();
var rnd = new Random();
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
controller: myTextController,
),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed:
// _incrementCounter,
() {
setState(() {
myTextController.text = rnd.nextInt(1000000000).toString();
});
},
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
update 01/10/19: question was solved at How to insert two action at one button OnPress - delete widget and create it by new?

Flutter Desktop Application- NoSuchMethodError: Class "Window" has no istance getter 'locales'

I'm pretty new on Flutter and I trying to build a desktop app.
I did all steps of following guide: https://medium.com/flutter-community/flutter-from-mobile-to-desktop-93635e8de64e (Running your own project (compiling the Go desktop project))
Unfornatelly, when I try to run the code I have this error:
NoSuchMethodError: Class "Window" has no instance getter 'locales'
Receiver: Instance of Window
Tried calling: locales
Thank you in advance
The Operating System is Windows 8.1 and following the code. It is the starter one where I just added the debugDefaultTargetPlatformOverride part.
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart' show
debugDefaultTargetPlatformOverride;
void main(){
debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
methods.
);
}
}