Flutter BLoC mapEventToState вызывается только в первый раз для события и не вызывается каждый раз при следующем запуске этого события

Я имею 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 для каждой страницы?

2 ответа

Решение

Хорошо, я сам нашел ответ!

Проблема, конечно, как подразумевал Федерик Джонатан, - это случай блока. Я использую одноэлементный экземпляр, созданный пакетом flutter get_it. Что действительно полезно, если вы реализуете внедрение зависимостей (например, для чистой архитектуры).

Итак, единственный случай был проблемой.

К счастью, в пакете реализован изящный метод resetLazySingleton<T>.

Вызов этого при возврате сбрасывает блок, используемый в этом виджете. Поэтому, когда я снова перехожу на страницу задач, я работаю с тем же, но сброшенным экземпляром этого блока.

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

    return true;
  }

Я надеюсь, что этот ответ поможет кому-то, у кого проблемы с синглетонами, инъекциями зависимостей и переходом взад и вперед в приложении flutter с bloc.

для всех, у кого есть аналогичная проблема:

если вы слушаете поток репозитория и перебираете излучаемый объект, это вызывает 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));
  },
Другие вопросы по тегам