【问题标题】:Flutter StreamProvider not updating after pushReplacement called调用 pushReplacement 后 Flutter StreamProvider 未更新
【发布时间】:2020-10-26 14:01:30
【问题描述】:

我有一个实现 firebase 身份验证的颤振应用。遵循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 由以下 get 函数作为流返回:

// 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() 屏幕导航的布局如下:

  • 首页()
    • 第二页()
      • 第三页()

我在前进时使用 Navigator.push()(即从“SecondPage()”到“ThirdPage()”)。然后,我将第二页和第三页脚手架包裹在 WillPopScope 中,onWillPop 调用 Navigator.pushReplacement() 来向后导航。

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

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

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

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

【问题讨论】:

    标签: flutter firebase-authentication flutter-provider


    【解决方案1】:

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

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

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

    我在signIn 页面中创建了if-else 声明,当用户返回非空值时,我再次调用Wrapper(),它成功了。

    【讨论】:

      【解决方案2】:

      我遇到了完全相同的问题,当用户以类似的方式注销时,我选择明确地导航回 Wrapper:

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

      话虽如此,如果有人能解释为什么在我们最初的案例中没有重建 Wrapper,我会很高兴。

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

      【讨论】:

        猜你喜欢
        • 2021-12-14
        • 2021-02-07
        • 2019-12-13
        • 2020-01-07
        • 2020-05-01
        • 2021-02-13
        • 1970-01-01
        • 2020-09-01
        • 2019-08-25
        相关资源
        最近更新 更多