Is your feature request related to a problem? Please describe.
Let's say I have a bloc, and a list page, the bloc has a state which contain data list and loading status, is it possible to bind data and loading status separately?
For example, using BlocListener to notify loading status change, and show/hide loading dialog.
At the same time, using BlocBuilder to notify data change and update ui.
They share the same bloc.
I know I can divide this bloc into two blocs, one for loading status and one for data list, but the loading status has to do with business logic inside data list bloc, it turns out to be nested blocs.
Describe the solution you'd like
My problem would be solved If one bloc can have two states, and I can use BlocListener to listen to one of state, and use BlocBuilder to listen to another.
Describe alternatives you've considered
Or do you have any advice to achieve what I want?
Thanks in advance.
Additional context
N/A
There is no need for 2 states.
A bloc's "state" can be any type of object you want and isn't limited to a
single data type.
In your case, you have 2 pieces of data, a loading status and a list of data.
So by simply creating a class which contains both, you only have to worry about handling/manipulating a single state.
For example:
class MyBloc extends Cubit<MyBlocState> {
MyBloc() : super(MyBlocState(dataStatus: DataStatus.loading)); // this is the state's initializer
load() async {
// inform your layout that you are loading data
emit(state.copyWith(dataStatus: DataStatus.loading));
var data = await getSomeData();
// once data is loaded, add it to your state
emit(state.copyWith(dataStatus: DataStatus.loaded, data: data));
}
}
enum DataStatus { loading, loaded }
// this is your bloc's "state" class
class MyBlocState {
final List<dynamic> data;
final DataStatus dataStatus;
MyBlocState({this.data, this.dataStatus});
MyBlocState copyWith({
List<dynamic> data,
DataStatus dataStatus,
}) =>
MyBlocState(
data: data ?? this.data,
dataStatus: dataStatus ?? this.dataStatus,
);
}
and your layout
BlocConsumer<MyBloc, MyBlocState>(
listener: (context, state) {
if (state.dataStatus == DataStatus.loaded) {
// show/hide dialog
}
},
builder: (context, state) {
//
},
);
@hawkinsjb1 thanks so much for taking the time to answer, I really appreciate it! 馃檹
@Chren closing this for now but feel free to comment with additional questions and I'm happy to continue the conversation 馃憤
That's exactly what I want, thanks!
There is no need for 2 states.
A bloc's "state" can be any type of object you want and isn't limited to a
single data type.In your case, you have 2 pieces of data, a loading status and a list of data.
So by simply creating a class which contains both, you only have to worry about handling/manipulating a single state.For example:
class MyBloc extends Cubit<MyBlocState> { MyBloc() : super(MyBlocState(dataStatus: DataStatus.loading)); // this is the state's initializer load() async { // inform your layout that you are loading data emit(state.copyWith(dataStatus: DataStatus.loading)); var data = await getSomeData(); // once data is loaded, add it to your state emit(state.copyWith(dataStatus: DataStatus.loaded, data: data)); } } enum DataStatus { loading, loaded } // this is your bloc's "state" class class MyBlocState { final List<dynamic> data; final DataStatus dataStatus; MyBlocState({this.data, this.dataStatus}); MyBlocState copyWith({ List<dynamic> data, DataStatus dataStatus, }) => MyBlocState( data: data ?? this.data, dataStatus: dataStatus ?? this.dataStatus, ); }and your layout
BlocConsumer<MyBloc, MyBlocState>( listener: (context, state) { if (state.dataStatus == DataStatus.loaded) { // show/hide dialog } }, builder: (context, state) { // }, );
Most helpful comment
That's exactly what I want, thanks!