BlocProvider.of() 使用不包含 Bloc 的上下文调用 - 即使它包含 [英] BlocProvider.of() called with a context that does not contain a Bloc - even that it does
问题描述
首先,我知道 BLoC 应该如何工作,它背后的想法,我知道 BlocProvider()
和 BlocProvider.value()
构造函数之间的区别.
First of, I do know how BLoC suppose to work, the idea behind it and I know the difference between BlocProvider()
and BlocProvider.value()
constructors.
为简单起见,我的应用程序有 3 个页面,其中包含这样的小部件树:
For simplicity, my application has 3 pages with a widget tree like this:
App()
=> LoginPage()
=> HomePage()
=> UserTokensPage()
App()
=> LoginPage()
=> HomePage()
=> UserTokensPage()
我希望我的 LoginPage()
能够访问 UserBloc
因为我需要登录用户等.为此,我将 LoginPage()
builder 在 App()
小部件,如下所示:
I want my LoginPage()
to have access to UserBloc
because i need to log in user etc. To do that, I wrap LoginPage()
builder at App()
widget like this:
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
.我是这样做的:
That obviously works just fine. Then, if User logs in successfully, he is navigated to HomePage
. Now, I need to have access to two different blocs at my HomePage
so I use MultiBlocProvider
to pass existing UserBloc
further and create a brand new one named DataBloc
. I do it like this:
@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()
构造函数传递.我是这样做的:
This also works. Problem happens when from HomePage
user navigates to UserTokensPage
. At UserTokensPage
I need my already existing UserBloc
that I want to pass with BlocProvider.value()
constructor. I do it like this:
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
时,我收到错误消息:
When I press button to navigate to MyTokensPage
i get error with message:
════════ 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
小部件吗?我不明白我做错了什么.
What am I doing wrong? Is it because i have extracted PopupMenuButton
widget that somehow loses blocs? I don't understand what I can be doing wrong.
推荐答案
我修好了.在 App
小部件中,我使用
I fixed it. Inside App
widget i create LoginPage
with
home: BlocProvider<UserBloc>(
create: (context) => UserBloc(UserRepository()),
child: LoginPage(),
在 LoginPage
我只是简单地将 BlocBuilders
一个包装成另一个
At LoginPage
I simply wrap BlocBuilders
one into another
Widget build(BuildContext context) {
return BlocListener<UserBloc, UserState>(
listener: (context, state) {
if (state is UserAuthenticated) {
Navigator.of(context).push(
MaterialPageRoute<HomePage>(
builder: (_) => BlocProvider.value(
value: BlocProvider.of<UserBloc>(context),
child: BlocProvider<NewRelicBloc>(
create: (_) => NewRelicBloc(NewRelicRepository()),
child: HomePage(),
),
),
),
);
}
},
[...]
PopupMenuButton
使用
Navigator.of(context).push(
MaterialPageRoute<UserTokensPage>(
builder: (_) => BlocProvider.value(
value: BlocProvider.of<UserBloc>(context),
child: UserTokensPage(),
),
),
);
这解决了我所有的问题.
And that solved all my problems.
这篇关于BlocProvider.of() 使用不包含 Bloc 的上下文调用 - 即使它包含的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!