Flutter BLoC mapEventToState 仅在事件的第一次调用时才被调用,并且不会在下次触发该事件时调用
Posted
技术标签:
【中文标题】Flutter BLoC mapEventToState 仅在事件的第一次调用时才被调用,并且不会在下次触发该事件时调用【英文标题】:Flutter BLoC mapEventToState gets called only the first time for an event and not called each next time that event is fired 【发布时间】:2020-06-28 14:01:41 【问题描述】:我有Courses
和Tasks
。每个Course
有很多Tasks
。这就是为什么我在应用程序中使用不同的屏幕来显示课程列表,点击课程后,我将导航到下一个屏幕 - 任务列表。这是我的onTap
方法的课程列表:
onTap: ()
TasksPageLoadedEvent pageLoadedEvent =
TasksPageLoadedEvent(
courseId: state.courses[index].id,
truckNumber: this.truckNumber,
);
serviceLocator<TaskBloc>().add(pageLoadedEvent);
Routes.sailor(
Routes.taskScreen,
params:
Routes.courseNumber:
state.courses[index].courseNumber,
Routes.truckNumber: this.truckNumber,
Routes.courseId: state.courses[index].id,
,
);
我创建了一个TasksPageLoadedEvent
,将其传递给TaskBloc
,然后导航到“任务”页面。
这里是TaskBloc
以及它如何处理映射事件 - 状态:
@override
Stream<TaskState> mapEventToState(
TaskEvent event,
) async*
if (event is TasksLoadingEvent)
yield TasksLoadingState();
else if (event is TasksReloadingErrorEvent)
yield TasksErrorState();
else if (event is TasksFetchedFailureEvent)
yield TaskFetchedStateFailureState(error: event.failure);
else if (event is TasksPulledFromServerEvent)
yield TasksPulledFromServerState(
truckNumber: event.truckNumber,
courseNumber: event.courseNumber,
courseId: event.courseId,
);
else if (event is TasksPageLoadedEvent)
yield TasksLoadingState();
final networkInfoEither = await this.getNetworkInfoQuery(NoQueryParams());
yield* networkInfoEither.fold((failure) async*
yield TasksErrorState();
, (success) async*
if (success)
final getTasksEither = await getTasksQuery(
GetTasksParams(
truckNumber: event.truckNumber,
courseId: event.courseId,
),
);
yield* getTasksEither.fold((failure) async*
yield TaskFetchedStateFailureState(error: "coursesDatabaseError");
, (result) async*
if (result != null)
yield TasksFetchedState(tasks: result);
else
yield TaskFetchedStateFailureState(
error: "coursesFetchFromDatabaseError");
);
else
yield TasksNoInternetState();
);
当我导航到“任务”页面时,BlocBuilder
会检查状态并相应地处理建筑物。我有一个返回功能,可以导航回课程页面:
onPressed: ()
serviceLocator<CourseBloc>().add(
CoursesPageLoadedEvent(truckNumber: this.truckNumber),
);
Navigator.of(context).pop(true);
,
这会触发上一页的类似事件并重新加载。
如果我想参加另一门课程并查看其任务,就会出现我面临的问题。如果我点击列表中的另一个项目并因此触发一个新的TasksPageLoadedEvent
(具有新属性),则根本不会调用mapEventToState()
。
我之前在 BLoC 上遇到过类似的问题,但它们是关于 BlocListener
和扩展 Equatable
的状态。这就是为什么我的事件没有扩展Equatable
(尽管我不确定这是否是这里的问题)。但仍然没有任何反应。
这是我的活动:
abstract class TaskEvent
const TaskEvent();
class TasksPageLoadedEvent extends TaskEvent
final String truckNumber;
final int courseId;
TasksPageLoadedEvent(
this.truckNumber,
this.courseId,
);
class TasksFetchedFailureEvent extends TaskEvent
final String failure;
TasksFetchedFailureEvent(
this.failure,
);
class TasksLoadingEvent extends TaskEvent
class TasksReloadingErrorEvent extends TaskEvent
class TasksPulledFromServerEvent extends TaskEvent
final String courseNumber;
final String truckNumber;
final int courseId;
TasksPulledFromServerEvent(
@required this.courseNumber,
@required this.truckNumber,
@required this.courseId,
);
我应该如何使用每个页面的两个 BLoC 在两个页面之间来回处理?
【问题讨论】:
我建议你使用这个插件pub.dev/packages/flutter_bloc 但我是,这是我正在使用的插件。 它是否仍能正确导航到 TaskPage?如果是, blocbuilder好的,我自己找到了答案!
当然,正如 Federick Jonathan 所暗示的那样,问题是集团的实例。我正在使用由颤振包 get_it 创建的单例实例。如果您正在实现依赖注入(例如对于干净的架构),这非常有用。
所以一个实例就是问题所在。
幸运的是这个包已经实现了简洁的方法resetLazySingleton<T>
。
在返回时调用它会重置该小部件中使用的块。因此,当我再次导航到“任务”页面时,我正在使用该块的相同但重置实例。
Future<bool> _onWillPop() async
serviceLocator.resetLazySingleton<TaskBloc>(
instance: serviceLocator<TaskBloc>(),
);
return true;
我希望这个答案能帮助那些在单例、依赖注入和使用 bloc 的颤振应用程序中来回切换的人。
【讨论】:
您好,您是如何解决问题的。我遇到了同样的问题,我正在使用 get 构建依赖注入,但是当我尝试使用 resetLazySingleton 时出现错误。 你得到的错误是什么?也许它的文字/消息会有所帮助?【参考方案2】:对于其他有类似问题的人:
如果您正在侦听存储库流并循环通过发出的对象,则会导致 mapEventToState
被阻塞。因为循环永远不会结束。
Stream<LoaderState<Failure, ViewModel>> mapEventToState(
LoaderEvent event) async*
yield* event.when(load: () async*
yield const LoaderState.loadInProgress();
await for (final Either<Failure, Entity> failureOrItems in repository.getAll())
yield failureOrItems.fold((l) => LoaderState.loadFailure(l),
(r) => LoaderState.loadSuccess(mapToViewModel(r)));
);
你应该做什么而不是await for
流,监听流然后引发另一个事件,然后处理事件:
watchAllStarted: (e) async*
yield const NoteWatcherState.loadInProgress();
_noteStreamSubscription = _noteRepository.watchAll().listen(
(failureOrNotes) =>
add(NoteWatcherEvent.notesReceived(failureOrNotes)));
,
notesReceived: (e) async*
yield e.failureOrNotes.fold(
(failure) => NoteWatcherState.loadFailure(failure),
(right) => NoteWatcherState.loadSuccess(right));
,
【讨论】:
以上是关于Flutter BLoC mapEventToState 仅在事件的第一次调用时才被调用,并且不会在下次触发该事件时调用的主要内容,如果未能解决你的问题,请参考以下文章