因此,与大多数人一样,我是Bloc的新手,挥舞着飞镖,挥舞着头。我已经用Google搜索,浏览了这里的帖子,但还没有找到真正的答案。
所以这是关于用团块和颤动进行导航的。以登录为例。因此,有一个登录页面,后面有一个集团,有时有人按下按钮进行登录。
因此,我们可以在bloc中调用一个函数进行验证。我认为这违反严格的方法,但我看到有人这样做。但是,如果登录成功,您如何导航到下一个屏幕?您不应该在一个集团中导航吗?
但是,如果该登录页面正在使用StreamBuilder更改状态,那么您也不能在构建器中添加导航吗?您无法返回导航,而返回小部件。
可以在initstate中导航某个位置,但是可以在initstate中放置一个流生成器,以侦听bloc中状态的变化吗?
这一切现在有点令人困惑,但是我很坚持,因为应该是前进的方向...
谢谢 保罗
答案 0 :(得分:2)
BlocListener
是您可能需要的小部件。如果状态更改为(例如)LoginSuccess
,则块侦听器可以调用通常的Navigate.of(context)
。 example here shows the BlocListener
in action,但没有导航,而是显示了Snackbar。
另一个选择是将回调传递给事件。
BlocProvider.of<MyBloc>(context).add(MyEvent(
data: data,
onSuccess: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return HomePage();
}),
);
}));
答案 1 :(得分:2)
首先:如果没有业务逻辑,则无需进入YourBloc类。
但是不时需要某些用户的活动来执行 Bloc 类中的某些逻辑,然后Bloc类必须决定下一步要做的事情:只是重建小部件或< strong>显示对话框,甚至导航到下一条路线。在这种情况下,您必须向UI发送一些状态才能完成操作。 然后出现另一个问题:当Bloc发送State来显示吐司时,我该如何处理窗口小部件?
这是所有故事的主要问题。
许多答案和文章建议使用flutter_block。该库具有BlocBuilder和BlocListener。使用这些课程,您可以解决一些问题,但不能解决其中的100%。
在我的情况下,我使用BlocConsumer来管理 BlocBuilder 和 BlocListener ,并提供了一种管理状态的绝妙方法。
从文档中:
BlocConsumer<BlocA, BlocAState>(
listenWhen: (previous, current) {
// return true/false to determine whether or not
// to invoke listener with state
},
listener: (context, state) {
// do stuff here based on BlocA's state
},
buildWhen: (previous, current) {
// return true/false to determine whether or not
// to rebuild the widget with state
},
builder: (context, state) {
// return widget here based on BlocA's state
}
)
使用 BlocConsumer 可以看到,您可以过滤状态:您可以轻松定义状态以重建小部件,并可以定义状态以显示一些弹出窗口或导航至下一个屏幕。
答案 2 :(得分:1)
使BLoC的神话成为 前进的道路:没有完美的状态处理方法。 每种状态管理体系结构都比其他解决方案更好地解决了一些问题。总会有权衡取舍,在决定体系结构时要意识到这一点很重要。
通常,好的架构很实用:它具有可扩展性和可扩展性,而只需要最小的开销。 由于人们对实用性的观点各不相同,因此架构始终会涉及各种意见,因此,请仔细考虑以下内容,因为我将就如何为您的应用程序采用BLoC提出自己的看法。
BLoC是Flutter中非常有前途的状态管理方法,因为它具有一个标志性成分:流。 它们允许将UI与业务逻辑分离,并且它们与Flutter-ish的方法(当过时重新构建整个小部件子树)一起使用时效果很好。 因此,很自然,往返于BLoC的每次通信都应使用流,对吗?
+----+ Stream +------+
| UI | --------> | BLoC |
| | <-------- | |
+----+ Stream +------+
好吧,
要记住的重要一点是,状态管理体系结构是达到目的的一种手段;您不应该只是为了做到这一点而做事,而是要保持开放的态度并仔细评估每种选择的利弊。 我们将BLoC与UI分开的原因是,BLoC不需要关心UI的结构-它仅提供一些不错的简单流,而数据发生的一切都是UI的责任。
但是,尽管流已被证明是从BLoC到UI传输信息的绝佳方式,但它们却在另一个方向增加了不必要的开销:
流被设计为传输连续的数据流(甚至在名称中也是如此),但是大多数时候,UI只需要触发BLoC中的单个事件即可。这就是为什么有时您会看到某些Stream<void>
或类似的恶意解决方案¹,只是为了遵循严格的BLoC-y处理方式。
此外,如果我们根据来自BLoC的流推送新路由,则BLoC基本上可以控制UI流程-但是,我们试图避免的是拥有直接控制UI和业务逻辑的代码!
这就是为什么某些开发人员(包括我)只是打破了完全基于流的解决方案,并采用自定义方式从UI触发BLoC中的事件的原因。
就我个人而言,我只是使用方法调用(通常返回Future
)来触发BLoC的事件:
+----+ method calls +------+
| UI | ----------------> | BLoC |
| | <---------------- | |
+----+ Stream, Future +------+
在这里,BLoC返回Stream
的“实时”数据和Future
的数据作为方法调用的答案。
让我们看看如何解决您的示例:
Stream<bool>
用户是否登录的信息,甚至可以提供Stream<Account>
的信息,其中Account
包含用户的帐户信息。Future<void> signIn(String username, String password)
方法,如果登录成功,则该方法不返回任何内容,否则将引发错误。try {
setState(() => _isLoading = true); // This could display a loading spinner of sorts.
await Bloc.of(context).signIn(_usernameController.text, _passwordController.text);
Navigator.of(context).pushReplacement(...); // Push logged in screen.
} catch (e) {
setState(() => _isLoading = false);
// TODO: Display the error on the screen.
}
这样,您就可以很好地分离关注点:
Stream
和最后,我想指出的是,这只是一种可能的解决方案,它通过尝试在复杂应用程序中处理状态的不同方式随着时间而发展。 重要的是要了解有关状态管理如何工作的不同观点,因此我鼓励您深入研究该主题,也许是通过观看Google I / O的"Pragmatic State Management in Flutter"会话来进行。
¹尝试为BLoC动作提供多个参数时,它变得更加丑陋–因为这时您需要定义一个包装器类,以便将其传递给Stream。
²我做承认启动应用程序时它有点难看:您将需要某种启动屏幕,该启动屏幕仅检查BLoC的流并将用户重定向到基于他/她是否登录。发生该规则的异常是因为用户执行了一个操作(启动应用程序),但是Flutter框架不允许我们直接参与(据我所知,至少不是很优雅)。
答案 3 :(得分:0)
正如felangel在下面的issue中在Github中提到的那样,我们可以为此目的使用BlocListner。
BlocListener(
bloc: BlocProvider.of<DataBloc>(context),
listener: (BuildContext context, DataState state) {
if (state is Success) {
Navigator.of(context).pushNamed('/details');
}
},
child: BlocBuilder(
bloc: BlocProvider.of<DataBloc>(context),
builder: (BuildContext context, DataState state) {
if (state is Initial) {
return Text('Press the Button');
}
if (state is Loading) {
return CircularProgressIndicator();
}
if (state is Success) {
return Text('Success');
}
if (state is Failure) {
return Text('Failure');
}
},
}
)