Flutter BLoC mapEventToState仅在事件第一次被调用,而在下次该事件被触发时不会被调用

时间:2020-03-17 13:17:35

标签: flutter bloc

我有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来处理我的来回页面?

2 个答案:

答案 0 :(得分:0)

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

当然,正如费德里克·乔纳森(Federick Jonathan)所暗示的那样,问题是集团的实例。我正在使用由flutter包get_it创建的单例实例。如果您要实现依赖项注入(例如,用于干净的体系结构),这真的很有用。

所以一个实例就是问题。

幸运的是,该软件包已经实现了整洁的方法resetLazySingleton<T>

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

Future<bool> _onWillPop() async {
    serviceLocator.resetLazySingleton<TaskBloc>(
      instance: serviceLocator<TaskBloc>(),
    );

    return true;
  }

我希望这个答案可以帮助遇到单例,依赖注入以及在带有bloc的Flutter应用程序中来回走动的人。

答案 1 :(得分:0)

对于其他有类似问题的人:

如果您正在侦听存储库流并遍历发出的对象,则会导致 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));
  },