【问题标题】:Flutter - How to pass user data to all viewsFlutter - 如何将用户数据传递给所有视图
【发布时间】:2018-04-09 22:53:48
【问题描述】:

我是 Flutter 世界和移动应用程序开发的新手,并且正在为如何在我的应用程序中传递用户数据而苦恼。

我尝试了几件事,但没有一个看起来很好,我确信我应该遵循一些最佳实践模式。

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

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

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

初始化状态

我尝试过的一种方法是重写 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),
));

继承的小部件

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

这篇文章展示了使用InheritedWidget 的一个很好的模式。当我将继承的小部件放置在 MaterialApp 级别时,当身份验证状态更改时,子级不会更新(我确定我做错了)

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

对于最佳实践模式或用于示例的资源链接的任何建议,我将不胜感激。

【问题讨论】:

标签: flutter dart firebase-authentication pass-data


【解决方案1】:

您可以使用 GetX 包检查用户是否已登录,获取用户数据并在整个应用程序中访问这些数据

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 哦,有趣,谢谢你的例子!我最近的尝试是将Scaffold 包装在InheritedWidget 中,以便在drawerappBarbody 中提供信息。我一直在使用FutureBuilder 以及用于加载内容的继承小部件。
    • 您是否尝试过将 onAuthStateChanges 与 StreamBuilder 一起使用?
    • 我遇到了 InheritedWidget 的一个问题——它不能从 initState() 使用。将其移至didChangeDependencies() 会有所帮助,但随后它会被多次调用,即使小部件不再处于活动状态。就我而言,InheritedWidget 包含 API 包装器,因此该解决方案不起作用。
    【解决方案3】:

    我更喜欢使用带有定位器的服务,使用Flutter get_it

    如果您愿意,可以使用缓存数据创建一个 UserService:

    class UserService {
      final Firestore _db = Firestore.instance;
      final String _collectionName = 'users';
      CollectionReference _ref;
    
      User _cachedUser; //<----- Cached Here
    
      UserService() {
        this._ref = _db.collection(_collectionName);
      }
    
      User getCachedUser() {
        return _cachedUser;
      }
    
      Future<User> getUser(String id) async {
        DocumentSnapshot doc = await _ref.document(id).get();
    
        if (!doc.exists) {
          log("UserService.getUser(): Empty companyID ($id)");
          return null;
        }
    
        _cachedUser = User.fromDocument(doc.data, doc.documentID);
        return _cachedUser;
      }
    }
    

    然后创建创建定位器

    GetIt locator = GetIt.instance;
    
    void setupLocator() {
      locator.registerLazySingleton(() => new UserService());
    }
    

    并在 main() 中实例化

    void main() {
      setupLocator();
      new Routes();
    }
    

    就是这样!您可以使用以下方法在任何地方调用您的 Service + cachedData:

    .....
    UserService _userService = locator<UserService>();
    
    @override
    void initState() {
      super.initState();
      _user = _userService.getCachedUser();
    }
    

    【讨论】:

    • 当用户重新打开应用程序 _cachedUser 将被销毁时如何使用此方法?
    【解决方案4】:

    对于我懒惰的数学, 我只是创建像 userdata.dart 这样的新文件,然后将任何变量放在上面,例如 dynamic Profile = null

    userdata.dart

    //only put this or anything u want.
    dynamic Profile = null;
    

    startingpage.dart

    //import that file
    import '../userdata.dart';
    
    class startingpage extends ...{
    ...
    //set data to store..
       Profile = 'user profile';
    ...
    }
    

    使用数据只需声明和使用 anotherpage.dart

    //import that file
    import '../userdata.dart';
    
    class anotherpage extends...{
    ...
    }
    
    class .. State ...{
    ...
    //set the data to variable
       dynamic userdata = Profile;
       print('this is my lazy pass data' + userdata.toString());
    ...
    }
    

    【讨论】:

    • 你可以在里面给出分享偏好的例子。
    【解决方案5】:

    因为这个问题我遇到了另一个问题you can check it out here 所以我想出的解决方案有点不整洁,我创建了一个单独的Instance dart页面并导入到每个页面。

     GoogleSignInAccount Guser = googleSignIn.currentUser;
     FirebaseUser Fuser;
    

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

      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 个页面,而继承的小部件工作量太大

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-03-05
      • 2021-12-07
      • 1970-01-01
      • 2015-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-03
      相关资源
      最近更新 更多