Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter - How to pass user data to all views

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 of 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 the constructor

This works too but is a lot of boilerplate to pass the user through all routes, views, and states that might need to access them. Also, we can't just do popAndPushNamed when transitioning routes because we can't pass a 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.

like image 656
Aaron Avatar asked Oct 28 '17 13:10

Aaron


People also ask

How do you pass data between screens on a flutter?

Here we will assign a callback function to the onTap() function that uses the Navigator. push() method of the Navigator class to pass the data to the description screen as shown below: Dart.

How do you make a global variable in flutter?

Global variables are usually frowned upon. A recommended solution for flutter is a provider library. It's just a widget that you insert somewhere high in the widget tree and give it some value (object, class) to hold. Then you access the value deeper inside other widgets.


1 Answers

I'd recommend investigating inherited widgets further; the code below shows how to use them with asynchronously updating data:

import 'dart:convert';  import 'package:flutter/material.dart'; import 'package:http/http.dart' as http;  void main() {   runApp(new MaterialApp(       title: 'Inherited Widgets Demo',       theme: new ThemeData(         primarySwatch: Colors.blue,       ),       home: new Scaffold(           appBar: new AppBar(             title: new Text('Inherited Widget Example'),           ),           body: new NamePage()))); }  // Inherited widget for managing a name class NameInheritedWidget extends InheritedWidget {   const NameInheritedWidget({     Key key,     this.name,     Widget child}) : super(key: key, child: child);    final String name;    @override   bool updateShouldNotify(NameInheritedWidget old) {     print('In updateShouldNotify');     return name != old.name;   }    static NameInheritedWidget of(BuildContext context) {     // You could also just directly return the name here     // as there's only one field     return context.inheritFromWidgetOfExactType(NameInheritedWidget);   } }  // Stateful widget for managing name data class NamePage extends StatefulWidget {   @override   _NamePageState createState() => new _NamePageState(); }  // State for managing fetching name data over HTTP class _NamePageState extends State<NamePage> {   String name = 'Placeholder';    // Fetch a name asynchonously over HTTP   _get() async {     var res = await http.get('https://jsonplaceholder.typicode.com/users');     var name = json.decode(res.body)[0]['name'];     setState(() => this.name = name);    }    @override   void initState() {     super.initState();     _get();   }    @override   Widget build(BuildContext context) {     return new NameInheritedWidget(       name: name,       child: const IntermediateWidget()     );   } }  // Intermediate widget to show how inherited widgets // can propagate changes down the widget tree class IntermediateWidget extends StatelessWidget {   // Using a const constructor makes the widget cacheable   const IntermediateWidget();    @override   Widget build(BuildContext context) {     return new Center(       child: new Padding(         padding: new EdgeInsets.all(10.0),         child: const NameWidget()));   } }  class NameWidget extends StatelessWidget {   const NameWidget();    @override   Widget build(BuildContext context) {     final inheritedWidget = NameInheritedWidget.of(context);     return new Text(       inheritedWidget.name,       style: Theme.of(context).textTheme.display1,     );   } } 
like image 185
Matt S. Avatar answered Sep 28 '22 01:09

Matt S.