调用 pushReplacement 后 Flutter StreamProvider 未更新

Flutter StreamProvider not updating after pushReplacement called

我有一个实现 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 由以下 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() 页面)。感谢任何帮助。

我 运行 遇到了完全相同的问题,我选择在用户退出时明确导航回 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 中所述。这将简化并改善离线体验。

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

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

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

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