首页 文章

Flutter - 如何将用户数据传递给所有视图

提问于
浏览
14

我是一个新的移动应用程序开发的新手,并在我应该如何在我的应用程序中传递用户数据 .

我已经尝试了几件事,但似乎都没有,我确信我应该遵循最好的练习模式 .

因为它使示例更容易,我使用firebase进行身份验证 . 我目前有一个单独的登录路径 . 一旦我登录,我想在大多数视图中使用用户模型来检查显示内容的权限,在抽屉中显示用户信息等...

Firebase有 await firebaseAuth.currentUser(); 最佳做法是在您可能需要用户的任何地方调用它吗?如果是这样,这个电话的最佳位置在哪里?

flutter codelab显示了在允许写入之前验证用户的一个很好的示例 . 但是,如果页面需要检查auth以确定要构建的内容,则异步调用不能进入 build 方法 .

initState

我尝试过的一种方法是覆盖initState并启动调用以获取用户 . 当未来完成时,我调用 setState 并更新用户 .

FirebaseUser user;

    @override
    void initState() {
      super.initState();
      _getUserDetail();
    }

  Future<Null> _getUserDetail() async {
    User currentUser = await firebaseAuth.currentUser();
    setState(() => user = currentUser);
  }

这很有效,但似乎需要它的每个小部件的很多仪式 . 屏幕在没有用户的情况下加载时也会闪烁,然后在未来完成时与用户进行更新 .

将用户传递给构造函数

这也有效,但是有很多样板可以让用户通过可能需要访问它们的所有路由,视图和状态 . 此外,我们不能在转换路由时执行 popAndPushNamed 因为我们无法将变量传递给它 . 我们必须更改类似于此的路线:

Navigator.push(context, new MaterialPageRoute(
    builder: (BuildContext context) => new MyPage(user),
));

Inherited Widgets

https://medium.com/@mehmetf_71205/inheriting-widgets-b7ac56dbbeb1

本文展示了使用 InheritedWidget 的一个很好的模式 . 当我将继承的小部件放在MaterialApp级别时,子节点不是't updating when the auth state changed (I'我确定我做错了)

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似乎也是一个不错的选择,但似乎每条路线都有很多工作要做 . 在下面的部分示例中, _authenticateUser() 在完成时获取用户和设置状态 .

@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();
        }
      },
    );
  }

我很感激有关最佳实践模式的建议或用于示例的资源链接 .

2 回答

  • 0

    我建议进一步调查继承的小部件;下面的代码显示了如何使用它们异步更新数据:

    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,
        );
      }
    }
    
  • 15

    因为这个问题,我遇到了另一个问题you can check it out here所以我提出的解决方案有点凌乱,我创建了一个单独的Instance dart页面并将其导入到每个页面 .

    GoogleSignInAccount Guser = googleSignIn.currentUser;
     FirebaseUser Fuser;
    

    我在登录时将用户存储在那里并检查每个StateWidget是否为null

    Future<Null> _ensureLoggedIn() async {
    
    if (Guser == null) Guser = await googleSignIn.signInSilently();
    if (Fuser == null) {
      await googleSignIn.signIn();
      analytics.logLogin();
    }
    if (await auth.currentUser() == null) {
      GoogleSignInAuthentication credentials =
      await googleSignIn.currentUser.authentication;
      await auth.signInWithGoogle(
        idToken: credentials.idToken,
        accessToken: credentials.accessToken,
      );
    }
    

    这是我的旧代码,我在我当前的应用程序上清理了它,但我现在没有那个代码 . 只需检查空用户并再次登录

    我也为大多数Firebase实例做了这个,因为我的应用程序上有超过3个页面而且Inherited Widgets工作太多了

相关问题