首先,我知道BLoC应该如何工作,它背后的思想,我知道BlocProvider()
和BlocProvider.value()
构造函数之间的区别。
为简单起见,我的应用程序有3个页面,其中包含一个小部件树,如下所示:
第一个月第二个月第一个月=〉第一个月第三个月=〉第一个月第四个月=〉第一个月第五个月
我希望我的LoginPage()
能够访问UserBloc
,因为我需要登录用户等。为此,我在App()
小部件处 Package LoginPage()
构建器,如下所示:
void main() => runApp(App());
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
home: BlocProvider<UserBloc>(
create: (context) => UserBloc(UserRepository()),
child: LoginPage(),
),
);
}
}
显然,这样做效果很好。然后,如果 User 成功登录,他将被导航到HomePage
。现在,我需要访问HomePage
上的两个不同的块,所以我使用MultiBlocProvider
进一步传递现有的UserBloc
,并创建一个名为DataBloc
的全新块。我的操作如下:
@override
Widget build(BuildContext context) {
return BlocListener<UserBloc, UserState>(
listener: (context, state) {
if (state is UserAuthenticated) {
Navigator.of(context).push(
MaterialPageRoute<HomePage>(
builder: (_) => MultiBlocProvider(
providers: [
BlocProvider.value(
value: BlocProvider.of<UserBloc>(context),
),
BlocProvider<DataBloc>(
create: (_) => DataBloc(DataRepository()),
),
],
child: HomePage(),
),
),
);
}
},
[...]
这也是可行的。当从HomePage
user 导航到UserTokensPage
时会出现问题。在UserTokensPage
,我需要我已经存在的UserBloc
,我想用BlocProvider.value()
构造函数传递它。我这样做:
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: false,
title: Text('My App'),
actions: <Widget>[
CustomPopupButton(),
],
),
[...]
class CustomPopupButton extends StatelessWidget {
const CustomPopupButton({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return PopupMenuButton<String>(
icon: Icon(Icons.more_horiz),
onSelected: (String choice) {
switch (choice) {
case PopupState.myTokens:
{
Navigator.of(context).push(
MaterialPageRoute<UserTokensPage>(
builder: (_) => BlocProvider.value(
value: BlocProvider.of<UserBloc>(context),
child: UserTokensPage(),
),
),
);
}
break;
case PopupState.signOut:
{
BlocProvider.of<UserBloc>(context).add(SignOut());
Navigator.of(context).pop();
}
}
},
[...]
当我按下按钮导航到MyTokensPage
时,我收到错误消息:
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following assertion was thrown building Builder(dirty):
BlocProvider.of() called with a context that does not contain a Bloc of type UserBloc.
No ancestor could be found starting from the context that was passed to BlocProvider.of<UserBloc>().
This can happen if:
1. The context you used comes from a widget above the BlocProvider.
2. You used MultiBlocProvider and didn't explicity provide the BlocProvider types.
Good: BlocProvider<UserBloc>(create: (context) => UserBloc())
Bad: BlocProvider(create: (context) => UserBloc()).
The context used was: CustomPopupButton
我做错了什么?是因为我提取了PopupMenuButton
小工具,不知何故丢失了块吗?我不明白我能做错什么。
6条答案
按热度按时间cwtwac6a1#
您可以将需要通过应用程序访问的块 Package 在应用程序的入口点,如下所示
您可以通过以下方式在应用的任何位置访问此区块
第一个月
2guxujil2#
2022年3月10日编辑
由于这个线程变得非常流行,我觉得我需要添加一些评论。
如果您的目标是使用不是在
MaterialApp
小部件上方提供的块,而是通过用BlocProvider
Package 小部件(例如,某个页面)使该小部件能够访问该块来声明小部件树中的某个位置,则这是一个有效的解决方案。在
MultiBlocProvider
中声明所有的区块会更容易避免问题(就像我之前说的),但创建这个主题时并没有考虑到这一点。请随意投赞成票,并使用 Amesh Fernando 回复中描述的方法,但要知道两者的区别。我修复了它。在
App
小部件中,我使用在
LoginPage
中,我简单地将BlocBuilders
一个 Package 到另一个中PopupMenuButton
将 * 用户 * 导航到TokenPage
,使用我的问题都解决了。
gcmastyq3#
溶液
方法A:直接访问
UserBloc
提供程序示例而不传递它我更喜欢这个解决方案,因为它需要的代码更少。
A.1使用提供程序Consumer Package
CustomPopupButton
示例,以便每当UserBloc通知侦听器值发生更改时,该示例都会重建自身。更改此内容:
收件人:
A.2更改无状态小部件内的Provider示例调用,以禁用侦听值更改--
Consumer
已经完成了"侦听"和相应的"重建"。收件人:
收件人:
方法B:传递
UserBloc
提供程序示例与方法A相同,但:
userBloc
:return CustomPopupButton(userBloc: userBloc),
.CustomPopupButton
中声明final UserBloc userBloc;
成员属性。userBloc.add(SignOut());
而不是BlocProvider.of<UserBloc>(context, listen: false).add(SignOut());
解释
flutter_bloc
正在使用Provider
,要了解发生了什么,最好了解提供程序请参阅我对here回答,以了解我对您问题回答,并更好地了解提供程序和listen
标志cpjpxq1n4#
在bottomSheet或materialPageRoute中更改生成器中的上下文名称。
因此,块可以通过上下文访问父上下文,除非它要从构建器(底部表单)获取上下文。这可能会导致一个错误,即您无法访问块的示例。
ktecyv1j5#
您需要将小部件分解为两个小部件(出于可测试性原因,我建议这样做),或者使用Builder小部件获取子上下文。
来源:由Felix Angelov解决,https://github.com/felangel/bloc/issues/2064
xu3bshqb6#
您不必使用BlocProvider.value()来导航到另一个屏幕,您只需将MaterialApp Package 到BlocProvider中作为它的子项即可