Best Bloc state handling in Flutter
This is my first article about Flutter and everything I found a solution that was my challenge in Flutter.
however,
Now I want to show you how we can handle bloc state in the best way :)
first, we know in bloc state management our actions call() with Events and our data restore in the State of Bloc (we can use data of state in View).
Let's go to the point
For better connection of data in State with view, we need to know the stage of the event we called to receive data:
- initial
- loading
- Completed
- error
So, what is the solution to be able to handle all these modes in the design?
EventStatus
I created an abstract class named EventStatus to return the Status of State.
@immutable
abstract class EventStatus<T> {
final T? data;
final ErrorModel? message;
const EventStatus({
this.data,
this.message,
});
}
Now, we can create some classes that extend from EventStatus like these:
class EventLoading<T> extends EventStatus<T> {
const EventLoading({int? itemId}) : super(itemId: itemId);
}
class EventCompleted<T> extends EventStatus<T> {
const EventCompleted(T data) : super(data: data);
}
class EventInitial<T> extends EventStatus<T> {}
class EventError<T> extends EventStatus<T> {
const EventError({required ErrorModel message})
: super(
message: message,
);
}
So now you can use these classes to handle state, how?
here is an example for EventStatus:
class ShowState extends Equatable {
final EventStatus<ShowResponseEntity> showsStatus;
const ShowState({
required this.showsStatus,
});
@override
List<Object> get props => [
showsStatus,
];
ShowState copyWith({
EventStatus<ShowResponseEntity>? newShowsStatus,
}) {
return ShowState(
showsStatus: newShowsStatus ?? showsStatus,
);
}
}
in Show State, I created a method copyWith() for change status in Show Bloc
and we can handle the Status of State in Show Bloc like this:
part 'show_event.dart';
part 'show_state.dart';
class ShowBloc extends Bloc<ShowEvent, ShowState> {
final GetShowsUsecase showsUsecase;
ShowBloc({
required this.showsUsecase,
}) : super(
ShowState(
showsStatus: EventInitial(),
),
) {
on<GetShowsEvent>((event, emit) async {
emit(
state.copyWith(
newShowsStatus: const EventLoading(),
),
);
DataState<ShowResponseEntity> result =
await showsUsecase();
AppGlobal.config.copyWith(
newCinemaName: result.data?.place?.title,
);
return emit(
state.copyWith(
newShowsStatus: StatusHandler.getStatus(result),
),
);
});
}
}
when we call GetShowsEvent in design,
First, we send the state to the loading mode, and then we send the request to the domain layer. After that, when the data comes back from the higher layers, we update the state again based on the response model, for which I created a separate class to handle this issue:
class StatusHandler {
static EventStatus<T> getStatus<T>(
DataState<T> dataState, {
Function(T? data)? onSuccess,
Function(ErrorModel data)? onError,
}) {
if (dataState is DataSuccess) {
if (onSuccess != null) {
onSuccess(dataState.data);
}
return EventCompleted<T>(dataState.data as T);
} else {
if (onError != null) {
onError(dataState.error!);
}
return EventError<T>(message: dataState.error!);
}
}
}
Well, by now I hope you have understood what happens in Bloc, of course, you may be asked what is the DataState().
Just as I have a comprehensive class for managing state states in the block, I also have a class in the data layer that manages data. If you are interested, I can post the next article about this class.