将 BlocListener 与 Navigator PushNamed 一起使用会导致歧义
Posted
技术标签:
【中文标题】将 BlocListener 与 Navigator PushNamed 一起使用会导致歧义【英文标题】:Using BlocListener with Navigator PushNamed is causing ambiguity 【发布时间】:2021-09-09 05:00:18 【问题描述】:我在使用 bloc 侦听器进行导航时遇到问题
我有五个用于导航的屏幕,我在每个文件中都使用 BlocConsumer。我在颤振检查器中寻找粒度视图。我正在使用 blocListener 进行导航。当我 pushNamed 第一个屏幕一切正常,我导航到第二个屏幕(第一个屏幕添加到导航堆栈)。现在我在第二屏,当我按下移动到第三屏时,两个第二屏然后添加第三屏,堆栈应该是这样的(第一屏,第二屏,第三屏)但不幸的是就像 (第 1 个屏幕,第 2 个屏幕,第 2 个屏幕,第 3 个屏幕)。现在当我在第 3 个屏幕上并想添加第 4 个屏幕是堆栈但添加了第 2 个屏幕时,第 3 个屏幕添加了两个次然后添加第 4 个屏幕。堆栈应该是这样的 ( 1st screen , 2nd screen , 3rd screen , 4th screen ) 但不幸的是它就像 ( 1st screen , 2nd screen , 2nd screen , 3rd screen , 2nd screen ,第三屏,第三屏,第四屏)。因此,我在导航堆栈中有 8 个屏幕,而不是 4 个屏幕。
这是我在所有文件中使用的模式。
这是我创建 bloc 实例并关闭它的地方。
class MyAppRoutes
FieldsBloc _fieldsBloc = FieldsBloc();
Route onGenerateRoute(RouteSettings routeSettings)
try
switch (routeSettings.name)
case LandingPage.routeName:
return MaterialPageRoute(builder: (_) => LandingPage());
case CategoryPage.routeName:
return MaterialPageRoute(
builder: (context) => BlocProvider.value(
value: _fieldsBloc,
child: CategoryPage(),
));
case ExpertisePage.routeName:
return MaterialPageRoute(
builder: (context) => BlocProvider.value(
value: _fieldsBloc,
child: ExpertisePage(),
));
case ExpertiseLevelPage.routeName:
return MaterialPageRoute(
builder: (context) => BlocProvider.value(
value: _fieldsBloc,
child: ExpertiseLevelPage(),
));
case EducationPage.routeName:
return MaterialPageRoute(
builder: (context) => BlocProvider.value(
value: _fieldsBloc,
child: EducationPage(),
));
default:
return null;
catch (e)
print(e);
void dispose() async
_fieldsBloc.close();
这是我在每个文件中使用的小部件。
BlocConsumer<FieldsBloc, FieldsState>(builder: (context, state)
if (state is FieldsInitial)
return Container();
else if (state is FieldLoadingState)
return Padding(
padding: const EdgeInsets.all(8.0),
child: getCircularProgress(context),
);
else if (state is FieldSuccessfulState)
return Container();
else if (state is FieldUnsuccessfulState)
return Padding(
padding: const EdgeInsets.all(15.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.error,
color: Colors.red,
),
SizedBox(
width: 5.0,
),
Expanded(
child: TextStyleRes.textStyleFont1(
textColor: Colors.red,
text: state.message,
fontSize: 12,
fontWeight: FontWeight.w700)),
],
));
return Container();
, listener: (context, state)
if (state is FieldSuccessfulState)
return SchedulerBinding.instance.addPostFrameCallback((_)
Navigator.of(context).pushNamed(ExpertisePage.routeName);
);
),
这是正在触发的 Bloc 事件。
abstract class FieldsEvent
class NextButtonEventScreen3 extends FieldsEvent
List<String> categories;
NextButtonEventScreen3(this.categories);
class NextButtonEventScreen4 extends FieldsEvent
List skills;
NextButtonEventScreen4(this.skills);
class NextButtonEventScreen5 extends FieldsEvent
String expert;
NextButtonEventScreen5(this.expert);
这是集团。
if (event is NextButtonEventScreen3)
if (event.categories.isNotEmpty)
yield FieldLoadingState();
categories = event.categories;
yield FieldSuccessfulState();
else
throw ('Please choose at least 1 category');
//=====================SignUpScreen4===========================
else if (event is NextButtonEventScreen4)
if (event.skills.isNotEmpty)
yield FieldLoadingState();
skills = event.skills;
yield FieldSuccessfulState(_updateModel());
else
throw ('Please provide at least 1 skill');
//=====================SignUpScreen5===========================
else if (event is NextButtonEventScreen5)
expert = event.expert;
yield FieldSuccessfulState();
这些是集团中的国家
abstract class FieldsState
String message;
class FieldsInitial extends FieldsState
class FieldLoadingState extends FieldsState
class FieldSuccessfulState extends FieldsState
var data;
FieldSuccessfulState([this.data]);
class FieldUnsuccessfulState extends FieldsState
String message;
FieldUnsuccessfulState(this.message);
【问题讨论】:
这是单个FieldsBloc
服务所有三个屏幕吗?
是实际上 bloc 是相同的,但 blocConsumer 小部件在 5 个文件中被使用了 5 次
【参考方案1】:
导航堆栈上的先前页面仍然存在,正在监听 bloc 事件。当状态在第二个屏幕上变为FieldsSuccessfulState
时,两个侦听器都会看到这一点并尝试导航到下一个屏幕。
为确保只有当前屏幕会对FieldsSuccessfulState
做出反应,我可以想到两个选项:
将FieldsSuccessfulState
拆分为多个结果(不同的类或添加的字段),并使每个屏幕仅对其自身的成功状态作出反应。
在导航到下一个屏幕之前检查ModalRoute.of(context).isCurrent
。这可以在侦听器本身或listenWhen
参数中完成。
【讨论】:
以上是关于将 BlocListener 与 Navigator PushNamed 一起使用会导致歧义的主要内容,如果未能解决你的问题,请参考以下文章
为啥 Flutter 中的 BloCListener 不保存状态,认证过程中出现 setState() 问题?
Flutter 在 MultiBlocProvider 中使用 BlocListener 和 BlocBuilder