【问题标题】:Accessing currentUser data in firebase_auth plugin version 0.2.0在 firebase_auth 插件版本 0.2.0 中访问 c​​urrentUser 数据
【发布时间】:2018-01-31 03:17:34
【问题描述】:

在我的应用程序中,我有一个带有 UserAccountsDrawerHeader 的抽屉,我通过简单地从 FirebaseAuth.instance.currentUser.x 获取 x 属性来提供它的属性 p>

在最新的firebase_auth 0.2.0 版本中,currentUser() 是异步的。

我已经尝试了几个小时来存储当前登录用户的信息,但还没有找到正确的方法来做到这一点。

我知道我可以通过以下方式访问它们:

   Future<String> _getCurrentUserName() async {
  FirebaseUser user = await FirebaseAuth.instance.currentUser();
  return user.displayName;
}

...

new UserAccountsDrawerHeader(accountName: new Text(_getCurrentUserName()))

我知道这些代码 sn-ps 会导致类型不匹配,但我只是想说明我想要做什么。

究竟是什么让我无法找到解决方案?

更新

class _MyTabsState extends State<MyTabs> with TickerProviderStateMixin {
  TabController controller;
  Pages _page;
  String _currentUserName;
  String _currentUserEmail;
  String _currentUserPhoto;
  @override
  void initState() {
    super.initState();
    _states();
    controller = new TabController(length: 5, vsync: this);
    controller.addListener(_select);
    _page = pages[0];
  }

我的方法

我只是将身份验证状态与我之前实现的 TabBar 状态结合起来

   _states() async{
     var user = await FirebaseAuth.instance.currentUser();
     var name = user.displayName;
     var email = user.email;
     var photoUrl = user.photoUrl;
    setState(() {
      this._currentUserName=name;
      this._currentUserEmail=email;
      this._currentUserPhoto=photoUrl;
      _page = pages[controller.index];
    });
  }

我的抽屉

drawer: new Drawer(
        child: new ListView(
          children: <Widget>[
            new UserAccountsDrawerHeader(accountName: new Text(_currentUserName)  ,
              accountEmail: new Text (_currentUserEmail),
              currentAccountPicture: new CircleAvatar(
               backgroundImage: new NetworkImage(_currentUserPhoto),
              ),

这是我从调试控制台得到的异常

I/flutter (14926): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (14926): The following assertion was thrown building MyTabs(dirty, state: _MyTabsState#f49aa(tickers:
I/flutter (14926): tracking 1 ticker)):
I/flutter (14926): 'package:flutter/src/widgets/text.dart': Failed assertion: line 207 pos 15: 'data != null': is not
I/flutter (14926): true.
I/flutter (14926): Either the assertion indicates an error in the framework itself, or we should provide substantially

更新 2:

这就是我从 firebase 示例中修改 google 登录功能的方式:

    Future <FirebaseUser> _testSignInWithGoogle() async {
      final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
      final GoogleSignInAuthentication googleAuth =
      await googleUser.authentication;
//checking if there is a current user
      var check = await FirebaseAuth.instance.currentUser();
      if (check!=null){
        final FirebaseUser user = check;
        return user;
      }
      else{
      final FirebaseUser user = await _auth.signInWithGoogle(
        accessToken: googleAuth.accessToken,
        idToken: googleAuth.idToken,
      );
      assert(user.email != null);
      assert(user.displayName != null);
      assert(!user.isAnonymous);
      assert(await user.getToken() != null);

      return user;
    }
    }

更新 3:

我的主要功能

void main() {
      runApp(
          new MaterialApp(
        home: new SignIn(),
        routes: <String, WidgetBuilder>{
          "/SignUp":(BuildContext context)=> new SignUp(),
          "/Login": (BuildContext context)=> new SignIn(),
          "/MyTabs": (BuildContext context)=> new MyTabs()},

  ));
}

然后我的登录包含一个谷歌按钮,按下时:

onPressed: () {  _testSignInWithGoogle(). //async returns FirebaseUser
                          whenComplete(()=>Navigator.of(context).pushNamed("/MyTabs")
                          );
                        }

更新 1 中的 Drawer 包含在 MyTabs 构建中。

【问题讨论】:

    标签: dart firebase-authentication flutter dart-async


    【解决方案1】:

    有几种可能。

    首先:使用有状态的小部件 像这样覆盖 initState 方法:

    class Test extends StatefulWidget {
      @override
      _TestState createState() => new _TestState();
    }
    
    class _TestState extends State<Test> {
      String _currentUserName;
    
      @override
      initState() {
        super.initState();
        doAsyncStuff();
      }
    
      doAsyncStuff() async {
        var name = await _getCurrentUserName();
        setState(() {
          this._currentUserName = name;
        });
      }
    
    
      @override
      Widget build(BuildContext context) {
        if (_currentUserName == null)
          return new Container();
        return new Text(_currentUserName);
      }
    }
    

    第二:使用 FutureBuilder 小部件 基本上,它是那些不想使用有状态小部件的包装器。它最终也是如此。 但你将无法在其他地方重用你的未来。

    class Test extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return new FutureBuilder(
          future: _getCurrentUserName(),
          builder: (context, AsyncSnapshot<int> snapshot) {
            if (snapshot.hasData)
              return new Text(snapshot.data.toString());
            else
              return new Container();
          },
        );
      }
    }
    

    说明: 您的 getCurrentUserName 是异步的。 您不能直接将其与其他同步功能混合使用。 异步函数非常有用。但是如果你想使用它们,只需要记住两件事:

    在另一个异步函数中,您可以var x = await myFuture,它会等到myFuture 完成才能得到结果。

    但是你不能在同步函数中使用await。 相反,您可以使用 myFuture.then(myFunction)myFuture.whenComplete(myFunction)myFunction 将在未来完成时被调用。他们.then.whenComplete 都会将你未来的结果作为参数传递给你的myFunction

    【讨论】:

    • 我已经在使用带状态的 statefulwidget,在同一个类中为多个方法创建多个状态是否正确?还是我应该只更新现有状态?我还尝试在一个单独的班级中建造我的抽屉。这三个选项实际上可以工作,但在调试控制台上会出现一些错误,但首先我想了解如果您有多个状态,哪种方法是正确的?
    • Flutter 是关于组合的。如果您有像 Authentification 这样通用的东西,您可以考虑将该部分拆分为一个有状态的小部件。类似于:Root > ... > MyAuth > ... > SomethingThatNeedAuth。然后实现一个静态方法 MyAuth.of(BuildContext) => _MyAuthState。你应该很好。
    • 我不确定我理解创建静态方法的部分。
    • 我已经用我的代码更新了 OP,请检查一下。
    • 您的例外是因为您没有检查用户是否已登录。日志记录是异步的。这意味着您必须测试您是否已登录。
    【解决方案2】:

    “如何正确实施认证”? 你绝对不应该这样做。你会有大量的代码重复。

    组织 Authentification 等层的最理想方式是这样的:

    runApp(new Configuration.fromFile("confs.json",
      child: new Authentification(
        child: new MaterialApp(
          home: new Column(
            children: <Widget>[
              new Text("Hello"),
              new AuthentifiedBuilder(
                inRoles: [UserRole.admin],
                builder: (context, user) {
                  return new Text(user.name);
                }
              ),
            ],
          ),
        ),
      ),
    ));
    

    然后,当您需要配置或小部件内的当前用户时,您可以这样做:

    @override
    Widget build(BuildContext context) {
      var user = Authentification.of(context).user;
      var host = Configuration.of(context).host;
      // do stuff with host and the user
      return new Container();
    }
    

    这样做有很多好处,没有理由不这样做。 例如“一次编码,到处使用”。或者具有通用值并为特定小部件覆盖它的能力。 你会发现很多 Flutter 小部件都遵循这个想法。 比如导航器、脚手架、主题、...

    但是“如何做到这一点??” 这一切都归功于BuildContext context 参数。这提供了一些帮助来做到这一点。 例如,Authentification.of(context) 的代码如下:

    class Authentification extends StatefulWidget {
        final Widget child;
    
        static AuthentificationData of(BuildContext context) {
            final AuthentificationData auth = context.inheritFromWidgetOfExactType(AuthentificationData);
            assert(auth != null);
            return auth;
        }
    
        Authentification({this.child});
        @override
        AuthentificationState createState() => new AuthentificationState();
    }
    

    【讨论】:

    • 请问您,当您说“您绝对不应该这样做。”时,您是指检查当前用户是否登录的方式,还是实现的整个想法外部函数 _testSignInWithGoogle() 登录用户?
    • 这完全是关于“不要把土豆和苹果混在一起”。一个班级,一份工作。如果你不拆分东西,你在你的应用开发过程中会遇到很多问题。例如复制粘贴的代码,这使得事情难以维护/调试。另一个例子:如果你想用模拟用户添加单一测试怎么办?在我的示例中,我可以实例化一个继承自“Authentification”类的“TestAuth”类。覆盖一些方法。一切都完成了。但是你 ?您必须重写所有内容。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-06
    • 2019-04-14
    • 1970-01-01
    • 1970-01-01
    • 2020-07-21
    相关资源
    最近更新 更多