Flutter StreamProvider not updating after pushReplacement called

Issue

I have a flutter app that implements firebase authentication. After following this tutorial my main.dart file looks like this:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamProvider<User>.value(
      catchError: (_, err) => null,
      value: AuthService().user,
      child: MaterialApp(
        home: Wrapper(),
      ),
    );
  }
}

Where the value: AuthService().user is returned as a stream by the following get function:

// auth change user stream
  Stream<User> get user {
    return _auth.onAuthStateChanged.map(
      (FirebaseUser user) => _userFromFirebaseUser(user),
    );
  }

  User _userFromFirebaseUser(FirebaseUser user) {
    
    return user != null ? User(uid: user.uid) : null;
  }

And the Wrapper() widget looks like this:

class Wrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final user = Provider.of<User>(context);

    if (user == null) {
      print("user is null, should show Authenticate()");
      return Authenticate();
    } else {
      return Home();
    }
  }
}

This code functions when:

  1. Logging in from the Authenticate() widget
  2. logging out from the Home() screen.

However, from the Home() screen navigation is laid out as such:

  • Home()
    • SecondPage()
      • ThirdPage()

I use Navigator.push() when going forward (i.e. from "SecondPage()" to "ThirdPage()"). I then wrapped both Secondary and Third page scaffolds in a WillPopScope, where the onWillPop calls Navigator.pushReplacement() to navigate backwards.

(Note: I did this because these pages read and write data from firebase firestore, and Navigator.pop() does not cause a rerender. This means the user may update something on the ThirdPage() that changes a value in firestore but when I then call Navigator.pop() it doesn’t update the SecondPage() with the new data.)

My issue is, something about the Navigator.pushReplacement() when navigating from the SecondPage() to the Home() page is causing an issue so that when the user then logs out, the screen does not show the Authenticate() page.

I have placed a print("user is null, should show Authenticate()") call in the Wrapper() to verify that if (user == null) is true when the user clicks the logout button. It does print however it still doesn’t show the Authenticate() screen.

I know it has something to do with the pushReplacement(), as logging out directly from Home() without having navigated anywhere functions correctly (i.e. Authenticate() page is shown). Any help is appreciated.

Solution

I got the same issue while following the same tutorial. In my case, it turned out signUp button works fine but for both log-out and sign-in I need to refresh to see changes. After debugging a file I came to this conclusion.

Even though the stream always listens to the incoming data, it is inside the Widget build method and needs to be called to get another stream to. So I just needed to call the
Wrapper() method once I detect a non-null user value.
So I did this.

Navigator.pushAndRemoveUntil(context, MaterialPageRoute(builder: (context)=>
Wrapper()), (_) => false );

I made an if-else statement in the signIn page, and when user returned non-null I called the Wrapper() once again and it did the trick.

Answered By – Saurabh Kumar

Answer Checked By – Marilyn (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *