I'm new to the flutter world and mobile app development and struggling with how I should pass user data throughout my app.
I've tried several things, but none seem great and I'm sure there are best practice patterns I should be following.
Because it makes examples easier, I'm using firebase for authentication. I currently have a separate route for logging in. Once I'm logged in I want the User model in most views for checking permissions on what to show, displaying user info in the drawer, etc...
Firebase has an await firebaseAuth.currentUser();
Is it best practice to call this everywhere you might need the user? and if so, where is the best spot to place this call?
The flutter codelab shows a great example authenticating users before allowing writes. However, if the page needs to check auth to determine what to build, the async call can't go in the build
method.
initState
One method I've tried is to override initState and kick off the call to get the user. When the future completes I call setState
and update the user.
FirebaseUser user;
@override
void initState() {
super.initState();
_getUserDetail();
}
Future<Null> _getUserDetail() async {
User currentUser = await firebaseAuth.currentUser();
setState(() => user = currentUser);
}
This works decent, but seems like a lot of ceremony for each widget that needs it. There is also a flash when the screen loads without the user and then gets updated with the user upon the future's completion.
Pass the user through constructor
This works too, but is a lot of boilerplate to pass the user through all routes, views, and their states that might need to access them. Also, we can't just do popAndPushNamed
when transitioning routes because we can't pass variable to it. We have to change routes similar to this:
Navigator.push(context, new MaterialPageRoute(
builder: (BuildContext context) => new MyPage(user),
));
Inherited Widgets
https://medium.com/@mehmetf_71205/inheriting-widgets-b7ac56dbbeb1
This article showed a nice pattern for using InheritedWidget
. When I place the inherited widget at the MaterialApp level, the children aren't updating when the auth state changed (I'm sure I'm doing it wrong)
FirebaseUser user;
Future<Null> didChangeDependency() async {
super.didChangeDependencies();
User currentUser = await firebaseAuth.currentUser();
setState(() => user = currentUser);
}
@override
Widget build(BuildContext context) {
return new UserContext(
user,
child: new MaterialApp(
title: 'TC Stream',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new LoginView(title: 'TC Stream Login', analytics: analytics),
routes: routes,
),
);
}
FutureBuilder
FutureBuilder also seems like a decent option, but seems to be a lot of work for each route. In the partial example below, _authenticateUser()
is getting the user and setting state upon completion.
@override
Widget build(BuildContext context) {
return new FutureBuilder<FirebaseUser>(
future: _authenticateUser(),
builder: (BuildContext context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return _buildProgressIndicator();
}
if (snapshot.connectionState == ConnectionState.done) {
return _buildPage();
}
},
);
}
I'd appreciate any advice on best practice patterns or links to resources to use for examples.
For my lazy mathod, i just create new file like userdata.dart and then put any variable on it for example like dynamic Profile = null
inside userdata.dart
at startingpage.dart
to use the data just declare and use in anotherpage.dart
I crashed into another problem because of this problem you can check it out here So the solution I came up with is a bit untidy,I created a separate Instance dart page and imported it to every page.
I stored the user there on login and checked on every StateWidget if it was null
This is my old code I did cleaned it up on my current app but I don't have that code now in handy. Just check out for null user and log it in again
I did it for most of the Firebase instances too because I have more than 3 pages on my app and Inherited Widgets was just too much work
I'd recommend investigating inherited widgets further; the code below shows how to use them with asynchronously updating data:
I prefer to use Services with Locator, using Flutter get_it.
Create a UserService with a cached data if you like:
Then create create a Locator
And instantiate in main()
That's it! You can call your Service + cachedData everywhere using: