I'm trying to preserve the state of a widget, so that if I temporarily remove the stateful widget from the widget tree, and then re-add it later on, the widget will have the same state as it did before I removed it. Here's a simplified example I have:
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> {
bool showCounterWidget = true;
@override
Widget build(BuildContext context) {
return Material(
child: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
showCounterWidget ? CounterButton(): Text("Other widget"),
SizedBox(height: 16,),
FlatButton(
child: Text("Toggle Widget"),
onPressed: (){
setState(() {
showCounterWidget = !showCounterWidget;
});
},
)
],
),
),
);
}
}
class CounterButton extends StatefulWidget {
@override
_CounterButtonState createState() => _CounterButtonState();
}
class _CounterButtonState extends State<CounterButton> {
int counter = 0;
@override
Widget build(BuildContext context) {
return MaterialButton(
color: Colors.orangeAccent,
child: Text(counter.toString()),
onPressed: () {
setState(() {
counter++;
});
},
);
}
}
Ideally, I would not want the state to reset, therefor the counter would not reset to 0, how would I preserve the state of my counter widget?
The reason why the widget loose its state when removed from the tree temporarily is, as Joshua stated, because it loose its Element/State.
Now you may ask:
Can't I cache the Element/State so that next time the widget is inserted, it reuse the previous one instead of creating them anew?
This is a valid idea, but no. You can't. Flutter judges that as anti-pattern and will throw an exception in that situation.
What you should instead do is to keep the widget inside the widget tree, in a disabled state.
To achieve such thing, you can use widgets like:
These widgets will allow you to keep a widget inside the widget tree (so that it keeps its state), but disable its rendering/animations/semantics.
As such, instead of:
Widget build(context) {
if (condition)
return Foo();
else
return Bar();
}
which would make Foo
/Bar
loose their state when switching between them
do:
IndexedStack(
index: condition ? 0 : 1, // switch between Foo and Bar based on condition
children: [
Foo(),
Bar(),
],
)
Using this code, then Foo
/Bar
will not loose their state when doing a back and forth between them.