I have a widget that utilizes CourseBloc
class CourseBloc extends Bloc<CourseEvent, CourseState> {
final GetCoursesQuery getCoursesQuery;
final GetSettingsQuery getSettingsQuery;
final PullCoursesFromServerCommand pullCoursesFromServerCommand;
final UpdateTasksToServerCommand updateTasksToServerCommand;
final GetNetworkInfoQuery getNetworkInfoQuery;
CourseBloc({
@required GetCoursesQuery getCoursesQuery,
@required GetSettingsQuery getSettingsQuery,
@required PullCoursesFromServerCommand pullCoursesFromServerCommand,
@required UpdateTasksToServerCommand updateTasksToServerCommand,
@required GetNetworkInfoQuery getNetworkInfoQuery,
}) : assert(getCoursesQuery != null),
assert(getSettingsQuery != null),
assert(pullCoursesFromServerCommand != null),
assert(updateTasksToServerCommand != null),
assert(getNetworkInfoQuery != null),
this.getCoursesQuery = getCoursesQuery,
this.getSettingsQuery = getSettingsQuery,
this.pullCoursesFromServerCommand = pullCoursesFromServerCommand,
this.updateTasksToServerCommand = updateTasksToServerCommand,
this.getNetworkInfoQuery = getNetworkInfoQuery;
@override
CourseState get initialState => CourseInitialState();
@override
Stream<CourseState> mapEventToState(
CourseEvent event,
) async* {
if (event is CoursesReturnedFromTasksEvent) {
yield CourseInitialState();
} else if (event is CoursesPageLoadedEvent) {
yield CourseLoadingState();
final getCoursesEither = await getCoursesQuery(
GetCoursesParams(
truckNumber: event.truckNumber,
),
);
yield* getCoursesEither.fold((failure) async* {
yield CourseFetchedStateFailureState(error: "coursesDatabaseError");
}, (result) async* {
if (result != null) {
final getSettingsEither = await getSettingsQuery(NoQueryParams());
yield* getSettingsEither.fold((failure) async* {
yield CourseFetchedStateFailureState(error: "coursesDatabaseError");
}, (settingsResult) async* {
if (result != null) {
final networkInfoEither =
await this.getNetworkInfoQuery(NoQueryParams());
yield* networkInfoEither.fold((failure) async* {
yield CourseErrorState();
}, (success) async* {
yield CourseFetchedState(
settings: settingsResult,
courses: result,
isThereInternet: success,
);
});
} else {
yield CourseFetchedStateFailureState(
error: "coursesFetchFromDatabaseError");
}
});
} else {
yield CourseFetchedStateFailureState(
error: "coursesFetchFromDatabaseError");
}
});
} else if (event is CoursesRefreshButtonPressedEvent) {
yield CourseLoadingState();
final networkInfoEither = await this.getNetworkInfoQuery(NoQueryParams());
yield* networkInfoEither.fold((failure) async* {
yield CourseErrorState();
}, (success) async* {
if (success) {
final updateTasksToServerEither = await updateTasksToServerCommand(
UpdateTasksParams(truckNumber: event.truckNumber));
yield* updateTasksToServerEither.fold((failure) async* {
yield CourseFetchedStateFailureState(error: "coursesDatabaseError");
}, (result) async* {
final pullCoursesFromServerEither =
await pullCoursesFromServerCommand(
PullCoursesParams(truckNumber: event.truckNumber));
yield* pullCoursesFromServerEither.fold((failure) async* {
yield CourseFetchedStateFailureState(
error: "coursesDatabaseError");
}, (result) async* {
if (result != null) {
yield CoursePulledFromServerState();
} else {
yield CourseFetchedStateFailureState(
error: "coursesFetchFromDatabaseError");
}
});
});
} else {
yield CourseNoInternetState();
}
});
} else if (event is CoursesRefreshFromTasksButtonPressedEvent) {
serviceLocator<TaskBloc>().add(
TasksLoadingEvent(),
);
final networkInfoEither = await this.getNetworkInfoQuery(NoQueryParams());
yield* networkInfoEither.fold((failure) async* {
serviceLocator<TaskBloc>().add(
TasksReloadingErrorEvent(),
);
}, (success) async* {
if (success) {
final updateTasksToServerEither = await updateTasksToServerCommand(
UpdateTasksParams(truckNumber: event.truckNumber));
yield* updateTasksToServerEither.fold((failure) async* {
yield CourseFetchedStateFailureState(error: "coursesDatabaseError");
}, (result) async* {
final pullCoursesFromServerEither =
await pullCoursesFromServerCommand(
PullCoursesParams(truckNumber: event.truckNumber));
yield* pullCoursesFromServerEither.fold((failure) async* {
serviceLocator<TaskBloc>().add(
TasksFetchedFailureEvent(failure: "coursesDatabaseError"),
);
}, (result) async* {
if (result != null) {
serviceLocator<TaskBloc>().add(
TasksPulledFromServerEvent(
truckNumber: event.truckNumber,
courseNumber: event.courseNumber,
courseId: event.courseId,
),
);
} else {
serviceLocator<TaskBloc>().add(
TasksFetchedFailureEvent(
failure: "coursesFetchFromDatabaseError"),
);
}
});
});
} else {
yield CourseNoInternetState();
}
});
}
}
}
I use BlocBuilder
and BlocListener
in the widget page as follows:
BlocBuilder<CourseBloc, CourseState>(
builder: (context, state) {
if (state is CourseFetchedState) {
// here I have logic if the course is fetched
}
...
),
BlocListener<CourseBloc, CourseState>(
listener: (context, state) {
if (state is CourseNoInternetState) {
...
}
...
),
At some point I am navigating away of this widget. Then I want to come back to this first (Course) widget. I do:
serviceLocator<CourseBloc>().add(
CoursesReturnedFromTasksEvent(),
);
serviceLocator.resetLazySingleton<TaskBloc>(
instance: serviceLocator<TaskBloc>(),
);
Navigator.of(context).pop(true);
This tells the CourseBloc
to expect a CoursesReturnedFromTasksEvent()
, resets the new TaskBloc (because I am not on this page anymore and I don't need to know what state is it at) and pops the current context.
Then I am navigated back. The CourseBloc's mapping method works with the new state and according to the 'if' it yields:
if (event is CoursesReturnedFromTasksEvent) {
yield CourseInitialState();
}
But the Builder in the Course page is at the previous state. CourseFetchedState. And it has not 'taken' the new state (Initial).
Any ideas why that might happen?
Here are the States:
abstract class CourseState {
CourseState();
}
class CourseInitialState extends CourseState {}
class CourseReturnedFromTasksState extends CourseState {}
class CourseLoadingState extends CourseState {}
class CourseErrorState extends CourseState {}
class CourseNoInternetState extends CourseState {}
class CoursePulledFromServerState extends CourseState {}
class CourseFetchedState extends CourseState {
final SettingsAggregate settings;
final List<CourseAggregate> courses;
final bool isThereInternet;
CourseFetchedState({
@required this.settings,
@required this.courses,
@required this.isThereInternet,
});
}
class CourseFetchedStateFailureState extends CourseState {
final String error;
CourseFetchedStateFailureState({@required this.error});
}
And Events:
abstract class CourseEvent {
CourseEvent();
}
class CoursesRefreshButtonPressedEvent extends CourseEvent {
final String truckNumber;
CoursesRefreshButtonPressedEvent({@required this.truckNumber});
}
class CoursesRefreshFromTasksButtonPressedEvent extends CourseEvent {
final String courseNumber;
final String truckNumber;
final int courseId;
CoursesRefreshFromTasksButtonPressedEvent({
@required this.courseNumber,
@required this.truckNumber,
@required this.courseId,
});
}
class CoursesReturnedFromTasksEvent extends CourseEvent {}
class CoursesPageLoadedEvent extends CourseEvent {
final String truckNumber;
CoursesPageLoadedEvent({
this.truckNumber,
});
}
EDIT Here is how the Bloc is provided:
Column buildBody(BuildContext context) {
return Column(
children: <Widget>[
BlocProvider(
create: (_) => serviceLocator<CourseBloc>(),
child: BlocBuilder<CourseBloc, CourseState>(
builder: (context, state) {
if (state is CourseFetchedState) {
...
}
...
}
),
),
],
);
}
The serviceLocator()
is instantiated only once, at app startup. It registers all instances needed throughout the app life in order to achieve dependency injection. Here is the CourseBloc
registration:
import 'package:get_it/get_it.dart';
final serviceLocator = GetIt.instance;
...
serviceLocator.registerLazySingleton(
() => CourseBloc(
getCoursesQuery: serviceLocator(),
getSettingsQuery: serviceLocator(),
pullCoursesFromServerCommand: serviceLocator(),
updateTasksToServerCommand: serviceLocator(),
getNetworkInfoQuery: serviceLocator(),
),
);
...