Flutter : How to navigate to new page after state change?

Kyaw Tun picture Kyaw Tun · Aug 16, 2017 · Viewed 10.4k times · Source

How to navigate to new page after state change?

I had an app that require login first. Only after login, the app component are fully created. So I wrote something like this:

Main app

class AppComponentState extends State<AppComponent> implements CredentialProvider {

  Credential credential;

  @override
  Widget build(BuildContext context) {
    if (credential == null) {
      return new MaterialApp(
        routes: <String, WidgetBuilder>{
          '/': (BuildContext context) => new LoginPage(this),
        },
      );
    } else {
      return new MaterialApp(
        routes: <String, WidgetBuilder>{
          '/': (BuildContext context) => new Desktop(credential),
          ...
        },
      );
    }
  }
  @override
  void setCredential(Credential s) {
    setState(() {
      credential = s;
    });
  }
}

Credential provider interface

abstract CredentialProvider {
    void setCredential(Credential c);
}

The login button listener in LoginPage, credential is set and route to the new page.

@override
Widget build(BuildContext context) {

  void _handleSubmitted() {
    ...
    credentialProvider.setCredential(c); // this change main widget state and hence new pages are created
    // call setTimeout ?
    Navigator.pushNamed(context, "/"); // this should go to new page, because credential is set.
  }

  ...
}

It seem like, I have to wait current digest loop to finish before invoking Navigator.pushNamed(context, "/");. Is there any good way to do in flutter digest cycle like setTimeout in javascript?

It is a bit weird, conditional creation of MaterialApp. Is there better pattern in flutter?

Answer

Collin Jackson picture Collin Jackson · Aug 17, 2017

To answer your direct question, addPostFrameCallback or Future.delayed can call code after a minimal delay. However, there are some issues with this pattern:

  • I would recommend that you only have one MaterialApp widget. You can have a separate route for the login page.
  • In Flutter, state flows from parent to child. Children should not be calling methods on parent State. Instead the parent can pass callbacks to the children. Or perhaps a ChangeNotifier. If there's no other way to do what you want, you could use a GlobalKey to get access to the AppComponentState from any place in the app and call setCredential on it, but you're losing some encapsulation and testability by doing so.
  • Pushing another "/" on the navigator stack might cause issues if the user presses the back button and goes back to the previous place. Probably what you want is to trigger a rebuild of some widget that is a parent of LoginPage and Desktop. Or you can call runApp() again to force everything to rebuild.