Sta*_*ahp 26 navigation flutter bloc flutter-bloc
首先,我确实知道 BLoC 应该如何工作,它背后的想法以及我知道构造函数BlocProvider()
和BlocProvider.value()
构造函数之间的区别。
为简单起见,我的应用程序有 3 个页面,其中包含一个像这样的小部件树:
App()
=> LoginPage()
=> HomePage()
=>UserTokensPage()
我希望我LoginPage()
可以访问,UserBloc
因为我需要登录用户等。为此,我将LoginPage()
构建器包装在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(),
),
);
}
}
Run Code Online (Sandbox Code Playgroud)
这显然工作得很好。然后,如果用户成功登录,他将被导航到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(),
),
),
);
}
},
[...]
Run Code Online (Sandbox Code Playgroud)
这也有效。当从HomePage
用户导航到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();
}
}
},
[...]
Run Code Online (Sandbox Code Playgroud)
当我按下按钮导航到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
Run Code Online (Sandbox Code Playgroud)
我究竟做错了什么?是因为我提取PopupMenuButton
了不知何故丢失了块的小部件?我不明白我做错了什么。
Sta*_*ahp 19
我修好了它。内部App
窗口小部件我创建LoginPage
与
home: BlocProvider<UserBloc>(
create: (context) => UserBloc(UserRepository()),
child: LoginPage(),
Run Code Online (Sandbox Code Playgroud)
在LoginPage
我只是将BlocBuilders
一个包装成另一个
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(),
),
),
),
);
}
},
[...]
Run Code Online (Sandbox Code Playgroud)
PopupMenuButton
可前往[用户以TokenPage
与
Navigator.of(context).push(
MaterialPageRoute<UserTokensPage>(
builder: (_) => BlocProvider.value(
value: BlocProvider.of<UserBloc>(context),
child: UserTokensPage(),
),
),
);
Run Code Online (Sandbox Code Playgroud)
这解决了我所有的问题。
小智 17
您可以像这样将您需要通过应用程序访问的 Blocs 包装在应用程序的入口点
runApp(
MultiBlocProvider(
providers: [
BlocProvider<UserBloc>(
create: (context) =>
UserBloc(UserRepository()),
),
],
child: App()
)
);
}
Run Code Online (Sandbox Code Playgroud)
你可以在你的应用程序的任何地方访问这个块
BlocProvider.of<UserBloc>(context).add(event of user bloc());
Moh*_*far 11
更改构建器中上下文的名称,无论是在bottomSheet还是materialPageRoute中。
因此,该块可以通过上下文访问父上下文,除非它要从构建器(底部表格)获取上下文。这可能会导致您无法访问 bloc 实例的错误。
showModalBottomSheet(
context: context,
builder: (context2) { ===> change here to context2
BlocProvider.value(
value: BlocProvider.of<BlocA>(context),
child: widgetA(),
),
}
Run Code Online (Sandbox Code Playgroud)
UserBloc
直接访问provider实例,不传递我更喜欢这个解决方案,因为它需要更少的代码。
CustomPopupButton
使用提供者Consumer包装实例,以便在 UserBloc 通知侦听器值更改时重建自身。改变这个:
actions: <Widget>[
CustomPopupButton(),
],
Run Code Online (Sandbox Code Playgroud)
到:
actions: <Widget>[
Consumer<UserBloc>(builder: (BuildContext context, UserBloc userBloc, Widget child) {
return CustomPopupButton(),
});
],
Run Code Online (Sandbox Code Playgroud)
Consumer
.A.2.1改变这一点:
value: BlocProvider.of<UserBloc>(context),
Run Code Online (Sandbox Code Playgroud)
到:
value: BlocProvider.of<UserBloc>(context, listen: false),
Run Code Online (Sandbox Code Playgroud)
A.2.2并更改此:
BlocProvider.of<UserBloc>(context).add(SignOut());
Run Code Online (Sandbox Code Playgroud)
到:
BlocProvider.of<UserBloc>(context, listen: false).add(SignOut());
Run Code Online (Sandbox Code Playgroud)
UserBloc
提供者实例与方法 A 相同,但:
userBloc
这样的:return CustomPopupButton(userBloc: userBloc),
。final UserBloc userBloc;
在里面声明成员属性CustomPopupButton
。userBloc.add(SignOut());
而不是BlocProvider.of<UserBloc>(context, listen: false).add(SignOut());
flutter_bloc
正在使用Provider
,要了解发生了什么,最好了解 Provider。请参考我在此处的回答以了解我对您问题的回答,并listen
更好地了解 Provider 和flag。
归档时间: |
|
查看次数: |
27597 次 |
最近记录: |