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));
},