Reputation: 956
I'm using Google Sign In to handle user authentication in a Flutter app. This works great for a single-view app (no routing), but as soon as I try to add some routes and manage the routes effectively things get pretty tricky.
Correct me if I'm wrong, but what I think I want is a stateful widget at the top level which controls my signed in variable and auth methods. This top level stateful widget also controls my routing. Here are some snippets from my current setup:
class SignInState extends State<SignIn> {
GoogleSignInAccount _currentUser;
...
Future<Null> _handleSignIn() async {
try {
await _googleSignIn.signIn();
} catch (error) {
print(error);
}
}
Future<Null> _handleSignOut() async {
_googleSignIn.disconnect();
}
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: _pageTitle,
home: new Home(),
routes: <String, WidgetBuilder> {
"/": (BuildContext context) => new Home(),
"/settings": (BuildContext context) => new Settings(),
"/login": (BuildContext context) => new Login()
},
...
);
}
}
My assumption here is that my state would get passed down into the nested widgets but I'm not seeing the _currentUser
variable inside Home()
for instance.
In other frameworks you can specify methods to run on route load, it'd be nice if there was an elegant way to check if _currentUser existed before loading a route so that I could push a new route if a user wasn't logged in.
If anyone has experience with combining routing and authentication in Flutter apps I'd really appreciate a step in the right direction. Most tutorials I've found are for one or the other.
Thanks!
Upvotes: 4
Views: 7256
Reputation: 41
How about adding:
static Widget checkAuth(Widget page) {
if (currentUser == null) {
return Login();
} else {
return page;
}
}
...
routes: <String, WidgetBuilder> {
"/": (BuildContext context) => checkAuth(Home()),
"/settings": (BuildContext context) => checkAuth(Settings()),
"/login": (BuildContext context) => new Login()
},
...
Upvotes: 4
Reputation: 21661
If you make your SignIn
widget an InheritedWidget
, and add some property to it containing your user state (lets say userState
), you can add a method like this to it:
static UserState of(BuildContext context, { bool nullOk: false }) {
assert(context != null);
assert(nullOk != null);
final SignIn signInWidget = context.inheritFromWidgetOfExactType(SignIn);
if (signInWidget != null)
return signInWidget.userState;
if (nullOk)
return null;
throw new FlutterError(
'Attempted to find UserState without a SignInWidget parent.\n'
'The context used was:\n'
' $context'
);
}
And then you can call
SignIn.of(context)
In your routes to get the UserState
.
There are other ways to do this - putting your data in a redux store for example, or creating chains of callbacks (e.g. exposing a callback on your SignInWidget
that gets passed along to child routes). This is probably the cleanest built-in way of doing it, especially if you plan to just have this widget live at the top of your tree anyway - but if you adopted some other state management strategy (like using redux), you'd probably want to consider using that.
Upvotes: 3