【发布时间】:2020-05-11 03:07:03
【问题描述】:
首先,我知道 BLoC 应该如何工作,它背后的想法,我知道 BlocProvider() 和 BlocProvider.value() 构造函数之间的区别。
为简单起见,我的应用程序有 3 个页面,其中包含这样的小部件树:
App() => LoginPage() => HomePage() => UserTokensPage()
我希望我的LoginPage() 能够访问UserBloc,因为我需要登录用户等。为此,我将LoginPage() builder 包装在App() 小部件上,如下所示:
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 小部件,它以某种方式丢失了块吗?我不明白我做错了什么。
【问题讨论】:
-
为什么需要 BlocListener 来导航到下一页?我认为这可以使用
BlocBuilder以更简单的方式完成,并在UserAuthenticated时返回您需要返回的页面 -
@StefanoSaitta 我这样做是因为这是官方 flutter_bloc documentation 为 BlocListeners 推荐的做法。
-
@Stahp 试试我的答案,让我知道结果。
标签: flutter navigation bloc flutter-bloc