Flutter sets textfield cursor to start when changing controller's text - flutter

I have a reusable text field class like so:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String value = "";
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: MyTextField(
value: value,
onChange: (val) {
setState(() {
value = val;
});
},
),
),
),
);
}
}
typedef ChangeCallback = void Function(String value);
class MyTextField extends StatefulWidget {
final ChangeCallback onChange;
final String value;
const MyTextField({this.onChange = _myDefaultFunc, this.value = ""});
static _myDefaultFunc(String value){}
#override
_MyTextFieldState createState() => _MyTextFieldState();
}
class _MyTextFieldState extends State<MyTextField> {
final controller = TextEditingController();
#override
void initState() {
controller.text = widget.value;
super.initState();
}
#override
void didUpdateWidget(covariant MyTextField oldWidget) {
controller.text = widget.value;
super.didUpdateWidget(oldWidget);
}
#override
Widget build(BuildContext context) {
return TextField(
controller: controller,
onChanged: (value) {
widget.onChange(value);
},
);
}
}
As you can see, if the value is changed then onChange callback is called and also the value is again sent back to TextField. The problem with this is every time I update the value, TextField sets the cursor always to the start. Probably because the updated value is sent back to the TextField everytime? Not sure. Can you help me with this?

No need to update values in didUpdateWidget
//#override
// void didUpdateWidget(covariant MyTextField oldWidget) {
// controller.text = widget.value;
// super.didUpdateWidget(oldWidget);
// }
I just commented-out this function and the code works fine
below is the complete code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String value = "";
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(children: [
SizedBox(height:50),
Text("text input: $value"),
SizedBox(height:50),
Center(
child: MyTextField(
value: value,
onChange: (val) {
setState(() {
value = val;
});
},
),
),
]),
),
);
}
}
typedef ChangeCallback = void Function(String value);
class MyTextField extends StatefulWidget {
final ChangeCallback onChange;
final String value;
const MyTextField({this.onChange = _myDefaultFunc, this.value = ""});
static _myDefaultFunc(String value) {}
#override
_MyTextFieldState createState() => _MyTextFieldState();
}
class _MyTextFieldState extends State<MyTextField> {
final controller = TextEditingController();
#override
void initState() {
controller.text = widget.value;
super.initState();
}
// #override
// void didUpdateWidget(covariant MyTextField oldWidget) {
// controller.text = widget.value;
// super.didUpdateWidget(oldWidget);
// }
#override
Widget build(BuildContext context) {
return TextField(
controller: controller,
onChanged: (value) {
widget.onChange(value);
},
);
}
}

Instead of using onChanged method to get the value of text field, pass a TextEditingController.
class MyTextField extends StatefulWidget {
final TextEditingController controller;
final String defaultValue;
const MyTextField({#required this.controller, this.defaultValue = ''});
#override
_MyTextFieldState createState() => _MyTextFieldState();
}
class _MyTextFieldState extends State<MyTextField> {
#override
void initState() {
widget.controller.text = widget.defaultValue;
super.initState();
}
#override
Widget build(BuildContext context) {
return TextField(
controller: widget.controller,
);
}
}
This way, you can declare a TextEditingController in the parent widget to get the value like you would normally do with Flutter's native TextFormField.

Related

Update variable outside a widget in Flutter?

Is it possible to update a variable outside a widget while calling it ?
Here's an example :
class Widget1 extends StatefulWidget {
#override
State<Widget1> createState() => _Widget1State();
}
class _Widget1State extends State<Widget1> {
String example = 'A';
#override
Widget build(BuildContext context) {
return Column(children: [
Text(example),
Widget2(example: example)
],);
}
}
class Widget2 extends StatefulWidget {
final String example;
Widget2({required this.example});
#override
State<Widget2> createState() => _Widget2State();
}
class _Widget2State extends State<Widget2> {
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => setState(() {
widget.example = 'B'
}),
child: Text('update !'),
);
}
}
The idea here is that I want to update example using a button outside the widget.
This code is not working : example = 'A' no matter if I click the button or not, but I don't understand why since I'm calling the same variable.
Is there a simple solution to achieve this ? (by simple, I mean without the need of Provider or else.)
You can use callback method. Parent widget needed to updated, so setState is needed to be trigger on Widget1.
class Widget1 extends StatefulWidget {
#override
State<Widget1> createState() => _Widget1State();
}
class _Widget1State extends State<Widget1> {
String example = 'A';
#override
Widget build(BuildContext context) {
return Column(
children: [
Text(example),
Widget2(
example: example,
callback: (p0) {
setState(() {
example = p0;
});
},
),
],
);
}
}
class Widget2 extends StatefulWidget {
final String example;
final Function(String) callback;
Widget2({
required this.example,
required this.callback,
});
#override
State<Widget2> createState() => _Widget2State();
}
class _Widget2State extends State<Widget2> {
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
widget.callback("new data");
},
child: Text('update !'),
);
}
}
You can use Notifiers, here is an example:
import 'package:flutter/material.dart';
class ExampleNotifier with ChangeNotifier {
String example = 'A';
ExampleNotifier();
setText(string x) {
example = x;
notifyListeners();
}
}
and then use it like:
class Widget1 extends StatefulWidget {
#override
State<Widget1> createState() => _Widget1State();
}
class _Widget1State extends State<Widget1> {
#override
Widget build(BuildContext context) {
var exampleNotifier = Provider.of<ExampleNotifier>(context);
return Column(
children: [
Text(exampleNotifier.example),
Widget2(),
],
);
}
}
class Widget2 extends StatefulWidget {
#override
State<Widget2> createState() => _Widget2State();
}
class _Widget2State extends State<Widget2> {
#override
Widget build(BuildContext context) {
var exampleNotifier = Provider.of<ExampleNotifier>(context, listen: false);
return ElevatedButton(
onPressed: () {
exampleNotifier.setText('B');
},
child: Text('update !'),
);
}
}
If you want to use setState, you can use this
class Widget1 extends StatefulWidget {
#override
State<Widget1> createState() => _Widget1State();
}
class _Widget1State extends State<Widget1> {
String example = 'A';
void changeExample() {
setState(() => example = "B");
}
#override
Widget build(BuildContext context) {
return Column(
children: [Text(example), Widget2(changeExample: changeExample)],
);
}
}
class Widget2 extends StatelessWidget {
final void Function() changeExample;
Widget2({required this.changeExample});
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: changeExample,
child: Text('update !'),
);
}
}

Flutter lifting the state up through multiple dynamically added widgets

I'm trying to build a parent widget that has a button, when clicked, it displays another widget with some text and a drop-down list. When the drop-down selection is changed, the text should change accordingly. I've included below a simplified code of what I'm trying to achieve which doesn't work. The state lifting up concept is something confusing for me as a newcomer to Flutter
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String text = "Empty";
void addWidget() {
setState(() {
widList.clear();
widList.add(MidWidget(
text: text,
setValue: selectValue,
));
});
}
void selectValue(String value) {
setState(() {
text = value;
});
}
List<Widget> widList = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(children: [
ElevatedButton(onPressed: addWidget, child: const Text("Add Widget")),
Column(
children: widList,
)
]),
),
);
}
}
class MidWidget extends StatelessWidget {
const MidWidget({super.key, required this.text, required this.setValue});
final String text;
final Function setValue;
#override
Widget build(BuildContext context) {
return Column(
children: [
Text(text),
LowestWidget(
dropDownValue: "First",
setValue: setValue,
),
],
);
}
}
////////////////////
///////////////////
///
class LowestWidget extends StatelessWidget {
LowestWidget(
{super.key, required this.dropDownValue, required this.setValue});
final List<String> items = ["First", "Second"];
final String dropDownValue;
final Function setValue;
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: dropDownValue,
icon: const Icon(Icons.arrow_downward),
onChanged: (String? value) {
setValue(value);
},
items: items.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
}
First of all, both MidWidget and LowestWidget need to be converted to StatefulWidget because we need state changes inside those widgets too.
Secondly, selectValue function should be in the MidWidget, not in the parent widget, because it attempts to change the state of text that has already been passed onto the MidWidget with its original value at the time of its instantiation. Any change in text via setState is not going to affect its value in MidWidget anymore.
Thirdly, I've introduced _value variable in both MidWidget and LowestWidget that takes its initial value from the respective parent widgets in initState and then gets value changes via setState that are then used to be displayed in Text widget in MidWidget and DropdownButton widget in LowestWidget.
Following is the revised code that is working as per your requirements. I've commented out the deletions so that you could relate it with the original code.
Hope it helps!
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String text = "Empty";
void addWidget() {
setState(() {
widList.clear();
widList.add(MidWidget(
text: text,
// setValue: selectValue,
));
});
}
// void selectValue(String value) {
// setState(() {
// text = value;
// });
// }
List<Widget> widList = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(children: [
ElevatedButton(onPressed: addWidget, child: const Text("Add Widget")),
Column(
children: widList,
)
]),
),
);
}
}
class MidWidget extends StatefulWidget {
const MidWidget({super.key, required this.text, /*required this.setValue*/});
final String text;
// final Function setValue;
#override
State<MidWidget> createState() => _MidWidgetState();
}
class _MidWidgetState extends State<MidWidget> {
String? _value;
void selectValue(String value) {
setState(() => _value = value);
}
#override
void initState() {
_value = widget.text;
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
children: [
Text(_value!),
LowestWidget(
dropDownValue: "First",
setValue: selectValue,
),
],
);
}
}
////////////////////
///////////////////
///
class LowestWidget extends StatefulWidget {
LowestWidget(
{super.key, required this.dropDownValue, required this.setValue});
final String dropDownValue;
final Function setValue;
#override
State<LowestWidget> createState() => _LowestWidgetState();
}
class _LowestWidgetState extends State<LowestWidget> {
final List<String> items = ["First", "Second"];
String? _value;
#override
void initState() {
_value = widget.dropDownValue;
super.initState();
}
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: _value,
icon: const Icon(Icons.arrow_downward),
onChanged: (String? value) {
setState(() => _value = value);
widget.setValue(value);
},
items: items.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
}

how to input a string to textfield

Is it possible that the last String from a list can be input automatically to a TextField?
The list is empty for first few seconds and it is changing. It is also initialize after build context.
If yes please provide a code because I am new to flutter.
I know how to change the text from a textfield by using TextEditingController.
final TextEditingController _controller = TextEditingController();
TextField(controller: _controller)
ElevatedButton(
onPressed: () {
const newText = 'Hello World';
final updatedText = _controller.text + newText;
_controller.value = _controller.value.copyWith(
text: updatedText,
selection: TextSelection.collapsed(offset: updatedText.length),
);
},
)
but it has a button, how can I automate this?
Yes you can do this by TextEditingController... Let's have a code example where you have a List of String.... e.g
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late TextEditingController _textEditingController;
final List<String> _exampleList = [];
#override
void initState() {
Future.delayed(
const Duration(seconds: 2),
() {
if (mounted) {
setState(() {
_exampleList.add("apple");
_textEditingController = TextEditingController(text: _exampleList.last);
});
}
},
);
_textEditingController = TextEditingController(text: "Loading ...");
super.initState();
}
#override
void dispose() {
_textEditingController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: TextFormField(
enabled: _exampleList.isNotEmpty,
controller: _textEditingController,
),
),
),
);
}
}
here is the result... this is just a simple explanation of how you can show last item of list in textfield

How to redraw StatefulWidget

On the example below, since MyStatefulWidget has a state, it doesn't matter if setState is called on _MyAppState, because it will not be redrawn.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
int value = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('App Example')),
body: Row(children:[
MyStatefulWidget(title: value.toString()),
RaisedButton(
textColor: Colors.white,
color: Colors.blue,
onPressed: (){setState(() { value+=1; });},
child: new Text("Add"),
)
]),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key, this.title}):super(key: key);
final String title;
#override
State<StatefulWidget> createState() {
return _MyStatefulWidgetState();
}
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
String title;
#override
void initState() {
super.initState();
if (widget.title!=null) {
title = widget.title;
} else {
title = "";
}
}
int value = 0;
#override
Widget build(BuildContext context) {
return Text(title);
}
}
If I used a StatelessWidget it'd be redrawn, but this is just an example, there are cases where I need to redraw a StatefulWidget when setState is called.
One option would be to give it a name and build it from the setState, but I need it to be draw in the place where it's draw right now.
Dartpad: https://dartpad.dev/968be8755d5deab1ca5c8c84a993eafc
You could directly use widget.title in the Text widget to update the counter on screen. Please see the code below :
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
void changeVal(int val) {
setState(() {
value = val;
});
}
int value = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('App Example')),
body: Row(children: [
MyStatefulWidget(
title: value.toString(),
groupValue: value % 10,
chnageVal: changeVal),
RaisedButton(
textColor: Colors.white,
color: Colors.blue,
onPressed: () {
setState(() {
value += 1;
});
},
child: const Text("Add"),
)
]),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key key, this.title, this.groupValue, this.chnageVal})
: super(key: key);
final String title;
final int groupValue;
final Function(int) chnageVal;
#override
State<StatefulWidget> createState() {
return _MyStatefulWidgetState();
}
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
//String title;
// #override
// void initState() {
// super.initState();
// if (widget.title!=null) {
// title = widget.title;
// } else {
// title = "";
// }
// }
//int value = 0;
List<int> numbers = List.generate(10, (index) => index);
#override
Widget build(BuildContext context) {
return Container(
width: 120,
child: Column(children: [
Text(widget.title),
...numbers
.map((number) => RadioListTile<int>(
title: Text('$number'),
value: number,
groupValue: widget.groupValue,
onChanged: (val) {
widget.chnageVal(val);
},
))
.toList()
]),
);
}
}
Just provide a unique key while calling MyStatefulWidget like MyStatefulWidget(key: UniqueKey(), title: value.toString()),.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
int value = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('App Example')),
body: Row(children: [
MyStatefulWidget(key: UniqueKey(), title: value.toString()),
RaisedButton(
textColor: Colors.white,
color: Colors.blue,
onPressed: () {
setState(() {
value += 1;
});
},
child: new Text("Add"),
)
]),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key, this.title}) : super(key: key);
final String title;
#override
State<StatefulWidget> createState() {
return _MyStatefulWidgetState();
}
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
String title;
#override
void initState() {
super.initState();
if (widget.title != null) {
title = widget.title;
} else {
title = "";
}
}
int value = 0;
#override
Widget build(BuildContext context) {
return Text(title);
}
}
To know more about key please go through this article.
I will recommend using Stream, better performance and not so hard to use for refresh partial UI.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
StreamController<int> _streamController = StreamController();
int value = 0;
#override
void dispose() {
_streamController.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home:
StreamBuilder<int>(
stream: _streamController.stream,
initialData: value,
builder:
(BuildContext context, AsyncSnapshot<int> snapshot) {
return Scaffold(
appBar: AppBar(title: Text('App Example')),
body: Row(children:[
MyStatefulWidget(title: value.toString()),
RaisedButton(
textColor: Colors.white,
color: Colors.blue,
onPressed: (){_streamController.sink.add(value++);},
child: new Text("Add"),
)
]),
);},),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key, this.title}):super(key: key);
final String title;
#override
State<StatefulWidget> createState() {
return _MyStatefulWidgetState();
}
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
String title;
#override
void initState() {
super.initState();
if (widget.title!=null) {
title = widget.title;
} else {
title = "";
}
}
int value = 0;
#override
Widget build(BuildContext context) {
return Text(title);
}
}

Flutter - Persistent State between Pages

I'm trying to store some state during page changes. So old data values are available when a page is reloaded.
I've been looking into PageStorage but haven't figured out how it works yet.
I'm writing into the storage with PageStorage.of(context)?.writeState(context, 'Some text is here', identifier: ValueKey('test')); and then unloading the page with the back button.
When I reload the page (with Navigator.of(context).push()), using PageStorage.of(context)?.readState(context, identifier: ValueKey('test')); just gives me null;
Here's a short sample that I wrote to demonstrate how I'm using it. Does anyone know what I'm doing wrong?
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
MyAppState createState() {
return new MyAppState();
}
}
class MyAppState extends State<MyApp> {
final PageStorageBucket _bucket = new PageStorageBucket();
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: PageStorage(
child: new MyHomePage(),
bucket: _bucket,
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Center(
child: FloatingActionButton(
onPressed: () {
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return new NewPage();
}));
},
),
);
}
}
class NewPage extends StatefulWidget {
NewPageState createState() => NewPageState();
}
class NewPageState extends State<NewPage> {
String _text = '';
#override
void initState() {
super.initState();
_text = PageStorage
.of(context)
?.readState(context, identifier: ValueKey('test'));
}
#override
Widget build(BuildContext context) {
return Center(
child: Column(
children: <Widget>[
Text('The text is $_text'),
FloatingActionButton(
onPressed: () {
setState(() {
PageStorage.of(context)?.writeState(
context, 'Some text is here',
identifier: ValueKey('test'));
});
},
)
],
),
);
}
}
There were multiple issues with the code you provided.
The first one being in your MyAppState where you didn't provided a key to your PageStorage. Indeed without the key , the written data cannot be saved and I quote :
writeState(BuildContext context, dynamic data, {Object identifier}) → void
package:flutter
Write the given data into this page storage bucket using the specified identifier or an identifier computed from the given context. The computed identifier is based on the PageStorageKeys found in the path from context to the PageStorage widget that owns this page storage bucket.
If an explicit identifier is not provided and no PageStorageKeys are found, then the data is not saved.
To resolve this just create a global variable PageStorageKey mykey = new PageStorageKey("testkey"); and pass it along the creation of your PageStorage:
class MyAppState extends State<MyApp> {
final PageStorageBucket _bucket = new PageStorageBucket();
#override
Widget build(context) {
return new MaterialApp(
home: PageStorage(
child: new MyHomePage(),
bucket: _bucket,
key: mykey,
),
);
}
}
Then use the same key again to write the data :
onPressed: () {
setState(() {
PageStorage.of(context).writeState(context, 'Data saved',
identifier: ValueKey(mykey));
updateText();
});
Finally the way you update the text is, in my opinion not the best way to do it.
You should create a method (updateText() for example) and call it after you wrote your data.
updateText() {
if (PageStorage.of(context) .readState(context, identifier: ValueKey(mykey)) != null) {
_text = PageStorage .of(context).readState(context, identifier: ValueKey(mykey));
}
else {
_text = 'PageStorageNull';
}
}
As always it's safer to check if the value is non-null to avoid errors.
Here is the full code :
void main() => runApp(new MyApp());
PageStorageKey mykey = new PageStorageKey("testkey");
class MyApp extends StatefulWidget {
#override
MyAppState createState() {
return new MyAppState();
}
}
class MyAppState extends State<MyApp> {
final PageStorageBucket _bucket = new PageStorageBucket();
#override
Widget build(context) {
return new MaterialApp(
home: PageStorage(
child: new MyHomePage(),
bucket: _bucket,
key: mykey,
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(context) {
return Center(
child: FloatingActionButton(
onPressed: () {
Navigator.push(context,
new MaterialPageRoute(builder: (context) => new NewPage()));
},
),
);
}
}
class NewPage extends StatefulWidget {
NewPageState createState() => NewPageState();
}
class NewPageState extends State<NewPage> {
String _text;
#override
void initState() {
super.initState();
}
updateText() {
if (PageStorage.of(context) .readState(context, identifier: ValueKey(mykey)) != null) {
_text = PageStorage .of(context).readState(context, identifier: ValueKey(mykey));
}
else {
_text = 'PageStorageNull';
}
}
#override
Widget build(context) {
return Center(
child: Column(
children: <Widget>[
Text('The text is $_text'),
FloatingActionButton(
onPressed: () {
setState(() {
PageStorage.of(context).writeState(context, 'Data saved',
identifier: ValueKey(mykey));
updateText();
});
},
)
],
),
);
}
}
With this code, press the button to go to the second page. On the second page press the button to update the text with the data provided in the writeState() method.
Hoping this can help you,
Regards
EDIT
Fist things first, sorry for misunderstanding the point.
And actually what you want is possible by using Buckets.
Indeed the : PageStorage .of(context).readState(context, identifier: ValueKey(mykey)); can be replace by :
_bucket.readState(context, identifier: ValueKey(mykey));
So what you have to do is make your _bucket variable global, then you need to wrap everything you have in your NewPageState within a PageStorage using the same Key and Bucket as your first PageStorage in the MyAppState
Doing so you will be able to read using the bucket too and keep your data through navigation.
Again he is the full code:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
PageStorageKey mykey = new PageStorageKey("testkey");
final PageStorageBucket _bucket = new PageStorageBucket();
class MyApp extends StatefulWidget {
#override
MyAppState createState() {
return new MyAppState();
}
}
class MyAppState extends State<MyApp> {
#override
Widget build(context) {
return new MaterialApp(
home: PageStorage(
child: new MyHomePage(),
bucket: _bucket,
key: mykey,
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(context) {
return Center(
child: FloatingActionButton(
onPressed: () {
Navigator.push(context,
new MaterialPageRoute(builder: (context) => new NewPage()));
},
),
);
}
}
class NewPage extends StatefulWidget {
NewPageState createState() => NewPageState();
}
class NewPageState extends State<NewPage> {
String _text;
#override
void initState() {
super.initState();
updateText();
}
updateText() {
if (_bucket.readState(context, identifier: ValueKey(mykey)) != null) {
_text = _bucket.readState(context, identifier: ValueKey(mykey));
}
else {
print(_bucket.toString());
}
}
#override
Widget build(context) {
return PageStorage(
key:mykey,
bucket: _bucket,
child: Column(
children: <Widget>[
Text('The text is $_text'),
FloatingActionButton(
onPressed: () {
setState(() {
_bucket.writeState(context, 'Data saved',
identifier: ValueKey(mykey));
updateText();
});
},
)
],
),
);
}
}