[英]Flutter BLoC: Navigator.pop in StreamBuilder in build() method
I'm following BLoC pattern and subscribing to stream, and reacting to state changes in build method.我遵循 BLoC 模式并订阅流,并对构建方法中的状态变化做出反应。 When data is loaded I want to close the screen.
加载数据后,我想关闭屏幕。
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Bloc'),
),
body: SafeArea(
child: StreamBuilder<UserState>(
stream: _userBloc.user,
initialData: UserInitState(),
builder: (context, snapshot) {
if (snapshot.data is UserInitState) {
return _buildInit();
}
if (snapshot.data is UserDataState) {
Navigator.pop(context, true);
return Container();
}
if (snapshot.data is UserLoadingState) {
return _buildLoading();
}
},
),
),
);
}
When I do Navigator.pop(context, true);
当我做
Navigator.pop(context, true);
in build()
method I get:在
build()
方法中我得到:
I/flutter ( 4360): ══╡ EXCEPTION CAUGHT BY ANIMATION LIBRARY ╞═════════════════════════════════════════════════════════
I/flutter ( 4360): The following assertion was thrown while notifying status listeners for AnimationController:
I/flutter ( 4360): setState() or markNeedsBuild() called during build.
I/flutter ( 4360): This Overlay widget cannot be marked as needing to build because the framework is already in the
I/flutter ( 4360): process of building widgets. A widget can be marked as needing to be built during the build phase
I/flutter ( 4360): only if one of its ancestors is currently building. This exception is allowed because the framework
I/flutter ( 4360): builds parent widgets before children, which means a dirty descendant will always be built.
I/flutter ( 4360): Otherwise, the framework might not visit this widget during this build phase.
What is the right way to handle such cases in BLoC pattern?在 BLoC 模式中处理此类情况的正确方法是什么?
On of the solutions I come up with is to start listening to stream on initState()
.我提出的解决方案之一是开始在
initState()
上收听流。 In this case I need to broadcast()
my stream because I have 2 subscribers.在这种情况下,我需要
broadcast()
我的流,因为我有 2 个订阅者。
Are there any better solutions for this?有没有更好的解决方案?
I think I have got a solution for you.我想我已经为你找到了解决方案。 (Please check it)
(请检查一下)
Make your code look like :让你的代码看起来像:
Widget build(BuildContext context) {
// other stuff
if (snapshot.data is UserDataState) {
myCallback(() {
Navigator.pop(context, true);
});
}
// other stuff
}
// after build method (but still in the same class,...) write below method
void myCallback(Function callback) {
WidgetsBinding.instance.addPostFrameCallback((_) {
callback();
});
}
Hope it helps.希望能帮助到你。 Just try it and please report here to help others too!
试试吧,请在此处报告以帮助他人!
Source (flutter_bloc Login medium article) 来源(flutter_bloc 登录中篇文章)
I could imagine three possible solutions:我可以想象三种可能的解决方案:
1) It looks to me like it would be best to restructure your widgets. 1) 在我看来,最好重组您的小部件。 As far as I can see you want a "Loading Screen" .. I see no reason that this has to be it's own navigation item, instead of just another widget.
据我所知,你想要一个“加载屏幕”..我认为没有理由这必须是它自己的导航项,而不仅仅是另一个小部件。
Ie. IE。 You could push the
StreamBuilder
one?你可以推
StreamBuilder
一个吗? level up.. so your builder method looks like:升级.. 所以你的构建器方法看起来像:
if (!snapshot.hasData) {
return LoadingScreen(snapshot);
}
// Whatever you do with your data.
2) I think i personally would create a StatefulWidget
, listen to the stream manually in initState()
and call setState()
manually. 2)我认为我个人会创建一个
StatefulWidget
,在initState()
手动收听流并手动调用setState()
。 No need for a StreamBuilder
不需要
StreamBuilder
3) as a crazy workaround you could probably use Future(() { Navigator.of(context).pop(); });
3) 作为一种疯狂的解决方法,您可能可以使用
Future(() { Navigator.of(context).pop(); });
in your builder.在你的建造者中。 (it's possible that you'd have to use the
context
of the build
method, not the builder. but I wouldn't recommend that solution anyway) (您可能必须使用
build
方法的context
,而不是构建器。但无论如何我都不推荐该解决方案)
bool hasPop = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Bloc'),
),
body: SafeArea(
child: StreamBuilder<UserState>(
stream: _userBloc.user,
initialData: UserInitState(),
builder: (context, snapshot) {
if (snapshot.data is UserInitState) {
return _buildInit();
}
if (snapshot.data is UserDataState) {
if(!hasPop){
hasPop = true;
Navigator.pop(context, true);
}
return Container();
}
if (snapshot.data is UserLoadingState) {
return _buildLoading();
}
},
),
),
);
} ```
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.