提问人:user1209216 提问时间:11/16/2023 最后编辑:Mark Rotteveeluser1209216 更新时间:11/17/2023 访问量:37
为命名路由 + 传递参数创建 bloc 实例
Create bloc instance for named route + pass arguments
问:
我的应用使用 FlutterBloc 进行状态管理。我的用例:
- 从 REST Web 服务加载的项目列表
- 在每个项目上,点击我需要加载项目详细信息并将其显示在新页面上。
我的代码在列表项上显示详细信息页面,点击:
return GestureDetector(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => BlocProvider(
create: (_) => DetailsBloc(
someRepository: context.read<SomeRepository>(),
otherRepository context.read<OtherRepository>(),
)..add(GetItemDetailsEvent(id: item.id.toString())),
child: const EventDetailsPage(),
),
),
);
},
child: Card(....)
它工作正常。在每次点击列表项时,它都会显示新页面,并为其上下文提供新实例。页面关闭后,bloc 也将被销毁(这就是我希望它的工作方式)。 包括按项目 ID 加载详细信息数据的事件,因此详细信息窗口能够自行刷新以显示详细信息。DetailsBloc
DetailsBloc
但我更喜欢使用命名路由并改用。当创建路由时,我能够以这种方式提供新实例,并可以选择向其发送事件:pushNamed
DetailsBloc
'/item_details': (context) => BlocProvider(
create: (context) => DataBloc(
someRepository: SomeRepository(),
otherRepository: OherRepository(),
)..add(GetItemDetailsEvent(id: <id>),
,
child: const DetailsPage()),
这样,我可以通过以下方式显示新页面 但在这种情况下,我认为无法将 id 传递给事件以加载数据,因为不知道路由何时初始化自身。所以我不能在路由定义中添加事件。我可以稍后尝试在列表项上添加事件:Navigator.of(context).pushNamed('/item_details');
onTap: () {
context.read<DetailsBloc>().add((GetItemDetailsEvent(id: item.id.toString());
这显然是行不通的,因为这里没有提供,在创建路由之前,它甚至不存在。我还可以将事件添加到我的内部,但它必须在内部初始化状态下完成,因此:DetailsBloc
DetailsBloc
DetailsPage
- 我的详细信息页面小部件需要是有状态的,我想避免
- 即使详细信息页面小部件是有状态的,我也无法读取 init 状态中的路由参数以将其传递给 bloc 的事件。所以基本上,我被卡住了,在这种情况下,我认为没有办法使用命名路由。
您有什么想法如何在这里使用命名路由吗?
答:
1赞
Vladyslav Ulianytskyi
11/17/2023
#1
您可以在路由器参数中传递 id:
await Navigator.pushNamed(
context,
DetailsPage.routeName,
arguments: id,
);
然后在route_builder中:
...
case DetailsPage.routeName:
final arguments = settings.arguments as String;
return MaterialPageRoute(
builder: (context) => DetailsPage(itemId: arguments),
settings: settings,
);
...
和 DetailsPage 中的 finaly:
class DetailsPage extends StatelessWidget {
const DetailsPage({
required this.itemId,
super.key,
});
static const routeName = '/details_page';
final int storyId;
@override
Widget build(BuildContext context) => BlocProvider<DetailsBloc>(
create: (context) => DetailsBloc(
someRepository: SomeRepository(),//or get it from DI or ServiceLocator
otherRepository: OtherRepository(),//or get it from DI or ServiceLocator
)..add(GetItemDetailsEvent(id: item.id.toString())),
child: const DetailsPageView(),
);
}
评论
0赞
user1209216
11/18/2023
是的,这也是我的想法。但我不喜欢我被迫使用param代替,而且我被迫在覆盖中创建bloc实例onGenerateRoute
routes
build
0赞
Vladyslav Ulianytskyi
11/18/2023
首先,创建 onGenerateRoute 是为了能够将参数传递给小部件。因此,正如您还提到的,您无法在您的情况下传递值,在创建路由之前不存在 DetailsBloc,并且您不希望它以这种方式运行。不太明白为什么你不喜欢 onGenerateRoute。作为拐杖:)您始终可以将 ID 存储在 SharedPreferences 中,并在需要时检索它:)
0赞
user1209216
11/18/2023
you always could store id in sharedpreferences and retrive it when you need it
- 这没有任何意义,因为每个列表项选项卡上的 ID 都会发生变化。你是对的,路线并没有那么糟糕。但是细节小部件比较复杂+在build方法中创建bloc实例非常糟糕,因为build方法不能保证只执行一次onGenerate
0赞
Vladyslav Ulianytskyi
11/19/2023
有点很奇怪的说法:“在构建方法中创建 bloc 实例非常糟糕,因为构建方法不保证只执行一次”从这个逻辑中我们得到:在父级的构建方法中创建任何 WIDGET 实例都是非常糟糕的,因为构建方法不保证只执行一次 %:) BlocBuilder 只是一个 Flutter 小部件。更多的信息: bloclibrary.dev/#/flutterbloccoreconcepts
0赞
user1209216
11/19/2023
是的,它是。但我实际上不想在每个小部件构建上创建新状态,因为我只需要向上下文提供和注入 bloc 一次。你真的不觉得在构建方法中创建新的 bloc 实例不安全吗?在我看来 - 获取价值:好的,创建实例 - 不是真的。最糟糕的场景:创建 bloc 实例 + 添加重建小部件的事件,我们得到无限循环
评论