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'));
});
},
)
],
),
);
}
}
By adding a PageStorage at the root and adding a PageStorageKey to each page, some of the page's state (e.g. the scroll position of a Scrollable widget) will be stored automatically in its closest ancestor PageStorage, and restored when it's switched back.
A storage bucket associated with a page in an app. Useful for storing per-page state that persists across navigations from one page to another.
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();
});
},
)
],
),
);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With