调用 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();
}
}
}
此代码在以下情况下起作用:
- 从 Authenticate() 小部件登录
- 正在从 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()
并且成功了。
我有一个实现 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();
}
}
}
此代码在以下情况下起作用:
- 从 Authenticate() 小部件登录
- 正在从 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()
并且成功了。