I have one StatefulWidget
in Flutter with button, which navigates me to another StatefulWidget
using Navigator.push()
. On second widge
This simple code worked for me to go to the root and reload the state:
...
onPressed: () {
Navigator.of(context).pushNamedAndRemoveUntil('/', ModalRoute.withName('/'));
},
...
There are 2 things, passing data from
Use this in 1st page
// sending "Foo" from 1st
Navigator.push(context, MaterialPageRoute(builder: (_) => Page2("Foo")));
Use this in 2nd page.
class Page2 extends StatelessWidget {
final String string;
Page2(this.string); // receiving "Foo" in 2nd
...
}
Use this in 2nd page
// sending "Bar" from 2nd
Navigator.pop(context, "Bar");
Use this in 1st page, it is the same which was used earlier but with little modification.
// receiving "Bar" in 1st
String received = await Navigator.push(context, MaterialPageRoute(builder: (_) => Page2("Foo")));
In short, you should make the widget watch the state. You need state management for this.
My method is based on Provider explained in Flutter Architecture Samples as well as Flutter Docs. Please refer to them for more concise explanation but more or less the steps are :
You could have multiple states say data
and isLoading
, to wait for some API process. The model itself extends ChangeNotifier
.
This could be Consumer
or Selector
.
For state model the class would look more or less as follows. Pay attention to notifyListeners
which broadcasts the changes.
class DataState extends ChangeNotifier{
bool isLoading;
Data data;
Future loadData(){
isLoading = true;
notifyListeners();
service.get().then((newData){
isLoading = false;
data = newData;
notifyListeners();
});
}
}
Now for the widget. This is going to be very much a skeleton code.
return ChangeNotifierProvider(
create: (_) => DataState()..loadData(),
child: ...{
Selector<DataState, bool>(
selector: (context, model) => model.isLoading,
builder: (context, isLoading, _) {
if (isLoading) {
return ProgressBar;
}
return Container(
child: Consumer<DataState>(builder: (context, dataState, child) {
return WidgetData(...);
}
));
},
),
}
);
Instance of the state model is provided by ChangeNotifierProvider. Selector and Consumer watch the states, each for isLoading
and data
respectively. There is not much difference between them but personally how you use them would depend on what their builders provide. Consumer provides access to the state model so calling loadData
is simpler for any widgets directly underneath it.
If not then you can use Provider.of
. If we'd like to refresh the page upon return from the second screen then we can do something like this:
await Navigator.push(context,
MaterialPageRoute(
builder: (_) {
return Screen2();
));
Provider.of<DataState>(context, listen: false).loadData();
You can use pushReplacement and specify the new Route
Put this where you're pushing to second screen (inside an async function)
Function f;
f= await Navigator.pushNamed(context, 'ScreenName');
f();
Put this where you are popping
Navigator.pop(context, () {
setState(() {});
});
The setState
is called inside the pop
closure to update the data.
The Easy Trick is to use the Navigator.pushReplacement method
Page 1
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => Page2(),
),
);
Page 2
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => Page1(),
),
);