Reputation: 19181
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:
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.
Upvotes: 2
Views: 614
Reputation: 3298
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
.
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.
Upvotes: 3