【问题标题】:How to properly setup authentication-based navigation如何正确设置基于身份验证的导航
【发布时间】:2018-02-14 05:46:12
【问题描述】:

我一直在尝试根据FirebaseAuth 的值找出通过我的应用设置导航的正确方法(即:仅在用户登录后导航)

所以我创建了一个场景,我有 3 个类,我将从列出我的测试用例开始,然后讨论我最后面临的问题。

User Class 是我的应用程序的第一页,它在检查是否有 currentUser 后绘制它是小部件

    class User extends StatefulWidget {
      @override
      _UserState createState() => new _UserState();
    }

    class _UserState extends State<User> {
       FirebaseUser _user;


      @override
      initState() {
        super.initState();
        _checkUser();
      }

      @override
      Widget build(BuildContext context) {
        return _user == null? new SignInPage(): new HomePage();
      }

      _checkUser ()async {
        FirebaseUser user = await FirebaseAuth.instance.currentUser();
        setState((){
          this._user = user;
        });

      }
    }

当应用第一次启动时,它会加载SignInPage,因为没有用户数据:

class SignInPage extends StatefulWidget {
  @override
  _SignInPageState createState() => new _SignInPageState();
}

class _SignInPageState extends State<SignInPage> {

  final FirebaseAuth _auth = FirebaseAuth.instance;
  final GoogleSignIn _googleSignIn = new GoogleSignIn();


   _handleSignIn() async{

    final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
    final GoogleSignInAuthentication googleAuth =
    await googleUser.authentication;
    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);
    print (user);

         }
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
            title: new Text("Sign In"),
            centerTitle: true,
            leading: new Container(),
            backgroundColor: new Color.fromARGB(255,17,165,137)),
        body: new Container(
            child: new Center(
                child:
                new Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    new Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      mainAxisSize: MainAxisSize.max,
                      children: <Widget>[new RaisedButton(
                          child: new Container(
                              child: new Row(children: <Widget>[
                                new Container(
                                    child: new Image(
                                      image: new NetworkImage(
                                          "https://maxcdn.icons8.com/Share/icon/Logos//google_logo1600.png"),
                                    ),
                                    margin: new EdgeInsets.only(right: 8.0)),
                                new Text("Login with Google", style: new TextStyle(color: Colors.redAccent)),
                              ], mainAxisAlignment: MainAxisAlignment.center)),
                          onPressed: (){_handleSignIn().whenComplete((){Navigator.of(context).pushNamed("/Home");});}

                          ,color: Colors.white),
                      ],),
                    new Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      mainAxisSize: MainAxisSize.max,
                      )


                  ],
                )
            )
        )
    );
  }


}

这是我的HomePage 类,只有通过验证才能访问;我的 Home 在中心包含一个RaisedButton,并有一个当前用户的displayNameTextchild,以及一个调用_signOut() 方法并返回到SignInageonPressed() 操作。

final GoogleSignIn _googleSignIn = new GoogleSignIn();


class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => new _HomePageState();
}

class _HomePageState extends State<HomePage> {
  FirebaseUser _currentUser;

  initState() {
    super.initState();
    _myUser();
  }
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Hello:)"),
      ),
      body: new Center(
        child: new RaisedButton(child: new Text(_currentUser.displayName),
            onPressed: (){_signOut().whenComplete((){Navigator.of(context).pushNamed("/SignInPage");});})
      ),
    );
  }
  _signOut() async {
    await FirebaseAuth.instance.signOut();

    await _googleSignIn.signOut();

    return "Signed out";
  }

   _myUser()async {
    FirebaseUser user = await FirebaseAuth.instance.currentUser();

    setState((){
      this._currentUser = user;
    });
  }


}

问题:

  1. 当应用程序第一次启动时,它会正常加载SignInPage,但是当我成功登录我的谷歌帐户时,它会在没有导航的情况下冻结,因为displayName was called on Null,据我了解,颤动没有在导航发生之前有足够的时间准备用户数据,我猜这是因为第 2 点和第 3 点发生的事情。

  2. 重新启动应用并保留之前的身份验证会话时,应用会自动以Home 页面启动,并且显示名称正确显示。

  3. 在同一个会话中,我单击 RaisedButton 退出并正确导航我回到 SignInPage,我尝试再次登录,我们又回到了主要问题,它试图导航我在获得displayName 之前到我的Home

这就是我对问题的理解,那么我在这里做错了什么以及如何设计正确的流程?

P.S:我之所以要这样做是因为我想在我的HomePage 中使用currentUser 的一些数据。

更新:

控制台打印以下内容:

'package:test/SignInScreen.dart': Failed assertion: line 26: 'user.email != null': is not true.

这是在_handleSignIn 中的assert 语句中。

我也尝试创建一个 pre-Home 页面,出现同样的问题,我不知道现在出了什么问题。

【问题讨论】:

    标签: navigation firebase-authentication flutter


    【解决方案1】:

    我建议使用FutureBuilderFirebaseAuth.instance.currentUser() 作为future 参数。然后在您的builder 方法中,如果AsyncSnapshothasData 属性为false,则您知道小部件仍在加载,您应该显示一个占位符。在Future 完成后,您可以使用AsyncSnapshotdata 属性来获取FirebaseUser

    或者,您可以在build 方法中检查_currentUser 是否为空,并返回一个空容器,因为用户信息尚不可用。

    【讨论】:

    • 我仍然遇到同样的问题,但是假设 hasData 为假,那么如何保持主页的加载直到它为真,什么样的占位符可以将我重定向到我的当 currentUser() 最终准备好时返回主页。如果您检查帖子中的更新部分,您会发现我什至试图在 SignInPage 之后放置一个虚拟页面只是为了为我的主页做好准备,但仍然是同样的问题。
    • 当我们检查 _currentUser 是否为 null 时,我们知道它为 null,至少对于当前运行以及在我重新启动应用程序之前,这是问题的核心。
    • 拜托,您是否设法重现了同样的问题,还是我遗漏了什么?
    • 我的错误是尝试使用FutureBuilder 并同时使用小部件的状态,现在它可以正常工作了。我最初以为我可以交替使用FutureBuilderState,但显然我不能。特别是使用State,我可以在另一个类中重用我的currentUser 数据,而不是为需要访问这些数据的每个类使用FutureBuilder。无论如何,感谢您总是如此乐于助人。
    • 您可以从 AsyncSnapshot 数据中获取用户并将其作为构造函数参数传递给其他小部件。实际上,您需要的是 Stream 而不是 Future,这是我一直在考虑的 API 的补充。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-02
    • 2017-09-06
    • 1970-01-01
    • 2019-02-02
    • 2016-10-31
    • 1970-01-01
    相关资源
    最近更新 更多