Force Flutter navigator to reload state when popping

前端 未结 14 2069
情书的邮戳
情书的邮戳 2020-11-28 22:54

I have one StatefulWidget in Flutter with button, which navigates me to another StatefulWidget using Navigator.push(). On second widge

相关标签:
14条回答
  • 2020-11-28 23:01

    This simple code worked for me to go to the root and reload the state:

        ...
        onPressed: () {
             Navigator.of(context).pushNamedAndRemoveUntil('/', ModalRoute.withName('/'));
                    },
        ...
    
    0 讨论(0)
  • 2020-11-28 23:08

    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")));
      
    0 讨论(0)
  • 2020-11-28 23:08

    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 :

    • Define your state model with states that the widget needs to observe.

    You could have multiple states say data and isLoading, to wait for some API process. The model itself extends ChangeNotifier.

    • Wrap the widgets that depend on those states with watcher class.

    This could be Consumer or Selector.

    • When you need to "reload", you basically update those states and broadcast the changes.

    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();
    
    
    0 讨论(0)
  • 2020-11-28 23:11

    You can use pushReplacement and specify the new Route

    0 讨论(0)
  • 2020-11-28 23:11

    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.

    0 讨论(0)
  • 2020-11-28 23:12

    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(),
      ),
    );
    
    0 讨论(0)
提交回复
热议问题