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 【问题描述】:

我有CoursesTasks。每个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 是否仍处于相同状态?你怎么知道 mapEventToState 根本没有被调用? 嗨!我希望你能看到我的问题 :) 是的,它可以导航。我放了一个断点。 BlocBuilder 处于“返回上一页”操作之前的最后一个状态。 哈哈.. 别对我抱太大期望。我不确定,但我假设 serviceLocator 提供了一个 TaskBloc 实例。话虽如此,您如何实现 BlocBuilder,因为据我所知,您不使用 BlocProvider 来提供 TaskBloc 实例。也许发生的事情是它在从 TaskPage 导航回来后使用了不同的 TaskBloc 实例 【参考方案1】:

好的,我自己找到了答案!

当然,正如 Federick Jonathan 所暗示的那样,问题是集团的实例。我正在使用由颤振包 get_it 创建的单例实例。如果您正在实现依赖注入(例如对于干净的架构),这非常有用。

所以一个实例就是问题所在。

幸运的是这个包已经实现了简洁的方法resetLazySingleton&lt;T&gt;

在返回时调用它会重置该小部件中使用的块。因此,当我再次导航到“任务”页面时,我正在使用该块的相同但重置实例。

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 仅在事件的第一次调用时才被调用,并且不会在下次触发该事件时调用的主要内容,如果未能解决你的问题,请参考以下文章

如何在新版本中访问 Flutter BLoC 状态值?

从 v7.2.1 迁移到 flutter_bloc v 8.0.0 后不会触发 flutter_bloc 事件

mapEventToState 仅触发一次

即使某些属性不相同,测试 bloc 状态也会通过

如何使用Bloc观察流的变化

谁能说出 Flutter 中“flutter_bloc”和“bloc”包的区别