Flutter StreamProvider在pushReplacement称为

时间:2020-07-06 12:32:09

标签: flutter firebase-authentication flutter-provider

我有一个实现Firebase身份验证的Flutter应用。遵循this教程之后,我的 main.dart 文件如下所示:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamProvider<User>.value(
      catchError: (_, err) => null,
      value: AuthService().user,
      child: MaterialApp(
        home: Wrapper(),
      ),
    );
  }
}

以下获取功能值:AuthService()。user 作为流返回的地方:

// auth change user stream
  Stream<User> get user {
    return _auth.onAuthStateChanged.map(
      (FirebaseUser user) => _userFromFirebaseUser(user),
    );
  }

  User _userFromFirebaseUser(FirebaseUser user) {
    
    return user != null ? User(uid: user.uid) : null;
  }

Wrapper()小部件如下所示:

class Wrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final user = Provider.of<User>(context);

    if (user == null) {
      print("user is null, should show Authenticate()");
      return Authenticate();
    } else {
      return Home();
    }
  }
}

此代码在以下情况下起作用:

  1. 从Authenticate()小部件登录
  2. 从Home()屏幕注销。

但是,从Home()屏幕导航的布局如下:

  • 首页()
    • SecondPage()
      • ThirdPage()

我在前进时(即从“ SecondPage()”到“ ThirdPage()”)使用了Navigator.push()。然后,我将辅助页面和第三页支架都包裹在 WillPopScope 中,其中 onWillPop 调用Navigator.pushReplacement()向后导航。

注意:之所以这样做,是因为这些页面从Firebase Firestore读取和写入数据,而Navigator.pop()不会导致重新渲染。这意味着用户可能会更新在Firestore中更改值的ThirdPage(),但是当我随后调用Navigator.pop()时,它不会使用新数据更新SecondPage()。)

我的问题是,从SecondPage()导航到Home()页面时,与Navigator.pushReplacement()有关的问题导致了问题,因此当用户随后注销时,屏幕上不显示Authenticate()页面。

我在用户单击注销按钮时在Wrapper()中发出了一个print("user is null, should show Authenticate()")调用,以验证if (user == null) true 。它可以打印,但是仍不显示Authenticate()屏幕。

我知道它与pushReplacement()有关,因为直接从Home()注销,而没有正确导航任何功能(即显示Authenticate()页面)。任何帮助表示赞赏。

2 个答案:

答案 0 :(得分:2)

我在学习相同的教程时遇到了同样的问题。就我而言,结果证明注册按钮工作正常,但对于注销和登录,我需要刷新以查看更改。在调试了一个文件后,我得出了这个结论。

尽管流总是监听传入的数据,但它在 Widget 构建方法中,需要调用以获取另一个流。所以我只需要调用 Wrapper() 方法一旦我检测到非空用户值。 所以我这样做了。

Navigator.pushAndRemoveUntil(context, MaterialPageRoute(builder: (context)=>
Wrapper()), (_) => false );

我在 if-else 页面中做了一个 signIn 语句,当用户返回非空值时,我再次调用了 Wrapper() 并且它成功了。

答案 1 :(得分:1)

我遇到了完全相同的问题,当用户注销时,我选择明确地导航回包装器:

  void signOut(BuildContext context) {
    Auth().handleSignOut();
    Navigator.pushAndRemoveUntil(
        context,
        MaterialPageRoute(builder: (context) => Wrapper()),
            (_) => false
    );
  }

出于安全原因,我仍在Provider中侦听流以使用户数据无效,但我不依赖于此侦听器来返回初始屏幕。

 void initAuthListener(){
    userStream = Auth().user;
    userStream.listen((data) {
      if(data==null){
        voidUser();
      }else{
        setUser(data);
      }
    }, onDone: () {
      print("Task Done");
    }, onError: (error) {
      print("Some Error");
    });
  }

说过,如果有人能阐明为什么在我们最初的案例中不重建包装纸,我会很高兴。

另一件事,您提到“我这样做是因为这些页面从Firebase Firestore读取和写入数据,而Navigator.pop()不会导致重新呈现。” 我建议您使用Streams从Firestore检索数据,并使用Streambuilders来显示数据,如get to know firebase playlist中所述。这样可以简化并改善离线体验。