Force Flutter navigator to reload state when popping

There's a couple of things you could do here. @Mahi's answer while correct could be a little more succinct and actually use push rather than showDialog as the OP was asking about. This is an example that uses Navigator.push:

import 'package:flutter/material.dart';

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.green,
      child: Column(
        children: <Widget>[
          RaisedButton(
            onPressed: () => Navigator.pop(context),
            child: Text('back'),
          ),
        ],
      ),
    );
  }
}

class FirstPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => new FirstPageState();
}

class FirstPageState extends State<FirstPage> {

  Color color = Colors.white;

  @override
  Widget build(BuildContext context) {
    return new Container(
      color: color,
      child: Column(
        children: <Widget>[
          RaisedButton(
            child: Text("next"),
            onPressed: () async {
              final value = await Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => SecondPage()),
                ),
              );
              setState(() {
                color = color == Colors.white ? Colors.grey : Colors.white;
              });
            },
          ),
        ],
      ),
    );
  }
}

void main() => runApp(
      MaterialApp(
        builder: (context, child) => SafeArea(child: child),
        home: FirstPage(),
      ),
    );

However, there's another way to do this that might fit your use-case well. If you're using the global as something that affects the build of your first page, you could use an InheritedWidget to define your global user preferences, and each time they are changed your FirstPage will rebuild. This even works within a stateless widget as shown below (but should work in a stateful widget as well).

An example of inheritedWidget in flutter is the app's Theme, although they define it within a widget instead of having it directly building as I have here.

import 'package:flutter/material.dart';
import 'package:meta/meta.dart';

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.green,
      child: Column(
        children: <Widget>[
          RaisedButton(
            onPressed: () {
              ColorDefinition.of(context).toggleColor();
              Navigator.pop(context);
            },
            child: new Text("back"),
          ),
        ],
      ),
    );
  }
}

class ColorDefinition extends InheritedWidget {
  ColorDefinition({
    Key key,
    @required Widget child,
  }): super(key: key, child: child);

  Color color = Colors.white;

  static ColorDefinition of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(ColorDefinition);
  }

  void toggleColor() {
    color = color == Colors.white ? Colors.grey : Colors.white;
    print("color set to $color");
  }

  @override
  bool updateShouldNotify(ColorDefinition oldWidget) =>
      color != oldWidget.color;
}

class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var color = ColorDefinition.of(context).color;

    return new Container(
      color: color,
      child: new Column(
        children: <Widget>[
          new RaisedButton(
              child: new Text("next"),
              onPressed: () {
                Navigator.push(
                  context,
                  new MaterialPageRoute(builder: (context) => new SecondPage()),
                );
              }),
        ],
      ),
    );
  }
}

void main() => runApp(
      new MaterialApp(
        builder: (context, child) => new SafeArea(
              child: new ColorDefinition(child: child),
            ),
        home: new FirstPage(),
      ),
    );

If you use inherited widget you don't have to worry about watching for the pop of the page you pushed, which will work for basic use-cases but may end up having problems in a more complex scenario.


Short answer:

Use this in 1st page:

Navigator.pushNamed(context, '/page2').then((_) => setState(() {}));

and this in 2nd page:

Navigator.pop(context);

There are 2 things, passing data from

  • 1st Page to 2nd

    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
    
        ...
      }
    

  • 2nd Page to 1st

    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")));
    

For me this seems to work:

Navigator.of(context).pushNamed("/myRoute").then((value) => setState(() {}));

Then simply call Navigator.pop() in the child.


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(),
  ),
);