Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I'm using flutter's StreamBuilder to decide which "root" page to show a user. Right now there are basically 2 possibilities - LoginPage or HomePage. My app's main build method looks like this:

  Widget build(BuildContext context) => StreamProvider.value(
      initialData: CurrentUser.initial,
      value: AuthService().user,
      child: Consumer<CurrentUser>(
          builder: (context, currentUser, _) => MaterialApp(
              home: currentUser.isInitialValue
                  ? Scaffold(
                      body: Center(
                        child: CircularProgressIndicator(),
                      ),
                    )
                  : currentUser.user != null
                      ? MultiProvider(providers: [
                          Provider<User>.value(value: currentUser.user),
                          // NOTE: Any other user-bound providers here can be added here
                        ], child: HomePage())
                      : LoginPage())));

The relevant part of the login page is that it gives you two options:

  1. "Are you a new user" - Sign Up
  2. "Are you returning" - Sign in

When you click one of those buttons, you go to the form in a certain mode via the navigator:

Navigator.push(
                                context,
                                CupertinoPageRoute(
                                  builder: (context) => LoginPage(
                                    mode: LoginPageMode.signUp,
                                  ),
                                ))

After successfully logging in or signing up, the stream gets updated and the HomePage renders. I know this because I have a print statement in the build method ("Building HomePage"):

class HomePage extends StatelessWidget {
  const HomePage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print('Building HomePage');
    return Scaffold(
        body: Container(
      alignment: Alignment.center,
      child: Text('HOME'),
    ));
  }
}

However, the screen does not actually change. The screen remains on the login page. I think I'm managing the stream fine because if I wasn't the render method would never be hit. I saw a similar question here but it seems they are not managing the stream properly. How can I see the render method hit, yet the screen stay the same? I've been working with Flutter for a while and I've never seen this before.

I think it has something to do with the navigation because if I remove the step where the user chooses "sign in" or "sign up", and I just send them to the sign in page, the issue disappears. It's like the HomePage is being built under the page that was navigated to.

question from:https://stackoverflow.com/questions/66058972/streambuilder-child-update-not-rendering-after-navigation

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
332 views
Welcome To Ask or Share your Answers For Others

1 Answer

1. Explanation

The reason of such behavior is that you use root Navigator that pushes LoginPage route on the apps root level. Here is widgets tree reveals widgets relations after push:

App
  |
  |-- StreamBuilder
  |      |--LoginPage()
  |
  |-- LoginPage(mode: LoginPageMode.signUp)

So when your StreamBuilder changes data the tree becomes this:

App
  |
  |-- StreamBuilder
  |      |--HomePage()    // <--- CHANGED
  |
  |-- LoginPage(mode: LoginPageMode.signUp)

Thats why you still see LoginPage and at the same time HomePage rendered too. HomePage just lays "under" LoginPage.

2. Solution

The solution is to use nested Navigator:


Widget build(BuildContext context) => StreamProvider.value(
      initialData: CurrentUser.initial,
      value: AuthService().user,
      child: Consumer<CurrentUser>(
          builder: (context, currentUser, _) => MaterialApp(
              home: currentUser.isInitialValue
                  ? Scaffold(
                      body: Center(
                        child: CircularProgressIndicator(),
                      ),
                    )
                  : currentUser.user != null
                      ? MultiProvider(providers: [
                          Provider<User>.value(value: currentUser.user),
                          // NOTE: Any other user-bound providers here can be added here
                        ], child: HomePage())
                      : Navigator(                //  <--- HERE
                          onGenerateRoute: (settings) {
                            return CupertinoPageRoute(
                              builder: (context) => LoginPage(),
                            );
                          },
                        )));

...

In that case when you will call Navigator.push(context, ...) inside LoginPage your widget tree will look like this:

App
  |
  |-- StreamBuilder
         |-- Navigator()
              |-- LoginPage()
              |-- LoginPage(mode: LoginPageMode.signUp)

Please try this approach, it should work.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share

548k questions

547k answers

4 comments

86.3k users

...