Hi, @felangel
I have a Todo BLoC, Category BLoC, Label BLoC. Each Todo reference to a Category and multi Labels, and Todo list has some filter properties.
I want to create a BLoC to manage what is displayed in Todo list.
I think I should make a BLoC that combines Loaded states of Todo BLoC, Category BLoC, Label BLoC and handles filter. But I see it's so complex.
Can you help me find the best practice?
So sorry for my bad English.
I am not sure what's the problem you are facing.
is it circular references?
then make a separate bloc for filtering and make others listen to it
Hi @bigworld12
I wonder if there are any ideas to make it simpler?
I was also confused when separating BLoC and creating Event for each BLoC.
BloCs are just sinks/streams for notifying the UI that a change has happened and for processing data from/into the UI
you don't NEED to provide an event for each bloc, heck you can have all events and states of type int, it's up to you.
you can provide some code as a start and I will see if i can help.
Hi @cver 馃憢
Thanks for opening an issue!
As @bigworld12 mentioned, it would be really helpful if you could provide some sample code. That way, we can take a look at an existing implementation and offer advice/give feedback 馃憤
Hi @felangel , @bigworld12
Can you tell me my code is good practice?
I'm so confused because I must generate a lot of code to do it. Thanks so much!
class TodoListBloc extends Bloc<TodoListEvent, TodoListState> {
TodoListBloc(TodoBloc todoBloc,
CategoryBloc categoryBloc,
LabelBloc labelBloc)
:
_todoBloc = todoBloc,
_categoryBloc = categoryBloc,
_labelBloc = labelBloc {
_todoSubscription = todoBloc.listen((state) {
if (state is TodosLoadedState)
_onTodoBlocLoaded(state);
});
}
final TodoBloc _todoBloc;
final CategoryBloc _categoryBloc;
final LabelBloc _labelBloc;
StreamSubscription _todoSubscription;
@override
TodoListState get initialState {
if (_todoBloc.state is TodosLoadedState
&& _categoryBloc.state is CategoriesLoadedState
&& _labelBloc.state is LabelsLoadedState) {
final todosLoadedState = _todoBloc
.state as TodosLoadedState;
final categoriesLoadedState = _categoryBloc
.state as CategoriesLoadedState;
final labelsLoadedState = _labelBloc
.state as LabelsLoadedState;
return TodoListLoadedState(
todoList: todosLoadedState.todos,
categoryMap: Map.fromIterable(
categoriesLoadedState.categories,
key: (item) => item.id, value: (item) => item),
labelMap: Map.fromIterable(
labelsLoadedState.labels,
key: (item) => item.id, value: (item) => item),
);
} else {
return TodoListLoadingState();
}
}
@override
Stream<TodoListState> mapEventToState(TodoListEvent event) {
if (event is TodoListLoadEvent) {
return _loadTodos(event);
}
return null;
}
@override
Future<void> close() {
_todoSubscription.cancel();
return super.close();
}
Stream<TodoListState> _loadTodos(
TodoListLoadEvent event) async* {
if (_todoBloc.state is TodosLoadedState
&& _categoryBloc.state is CategoriesLoadedState
&& _labelBloc.state is LabelsLoadedState) {
final todosLoadedState = _todoBloc
.state as TodosLoadedState;
final filteredTodos = todosLoadedState.todos.where((
item) {
final categoryCondition = event.categoryIds.isEmpty ||
event.categoryIds.contains(item.categoryId);
var labelCondition = false;
if (event.labelIds.isEmpty) {
labelCondition = true;
} else {
final index = event.labelIds.indexWhere((labelId) =>
item.labelIdList.contains(labelId));
if (index >= 0)
labelCondition = true;
}
return item.date >= event.fromDate
&& item.date <= event.toDate
&& categoryCondition
&& labelCondition;
});
final categoriesLoadedState = _categoryBloc
.state as CategoriesLoadedState;
final categoryMap = Map.fromIterable(
categoriesLoadedState.categories, key: (item) => item.id,
value: (item) => item);
final labelsLoadedState = _labelBloc.state as LabelsLoadedState;
final labelMap = Map.fromIterable(
labelsLoadedState.labels, key: (item) => item.id,
value: (item) => item);
yield TodoListLoadedState(
fromDate: event.fromDate,
toDate: event.toDate,
filteredCategoryIds: event.categoryIds,
filteredLabelIds: event.labelIds,
todoList: filteredTodos,
categoryMap: categoryMap,
labelMap: labelMap,
);
}
}
_onTodoBlocLoaded(TodosLoadedState state) {
if (state is TodoListLoadedState) {
final oldState = state as TodoListLoadedState;
final event = TodoListLoadEvent(
fromDate: oldState.fromDate,
toDate: oldState.toDate,
categoryIds: oldState.filteredCategoryIds,
labelIds: oldState.filteredLabelIds,
);
add(event);
}
}
}
@cver can you share a link to the complete app? It would be much easier to give feedback if we have the context of the whole application.
At first glance, it's unclear what the responsibilities of each of the blocs is and it seems like a fairly complex setup. I'm wondering if the bloc(s) can be simplified but would need to see the whole application to give better recommendations.
There are also little improvements you can make like rather than having to cast state you can create a currentState.
Before:
if (state is TodoListLoadedState) {
final oldState = state as TodoListLoadedState;
final event = TodoListLoadEvent(
fromDate: oldState.fromDate,
toDate: oldState.toDate,
categoryIds: oldState.filteredCategoryIds,
labelIds: oldState.filteredLabelIds,
);
add(event);
}
After:
final currentState = state;
if (currentState is TodoListLoadedState) {
final event = TodoListLoadEvent(
fromDate: currentState.fromDate,
toDate: currentState.toDate,
categoryIds: currentState.filteredCategoryIds,
labelIds: currentState.filteredLabelIds,
);
add(event);
}
Hope that helps 馃憤
Hi @felangel
Thanks so much for your support!
I think that BLoC class is most complex. For 3 other BLoCs TodoBloc, CategoryBloc, LabelBloc, I just put simple functions Create, Update, Delete.
@cver no problem!
Did you have any other specific questions? If so, are you able to share a link to the project? Thanks!
Closing for now but feel free to comment with additional information/questions and I'm happy to continue the conversation 馃憤
Most helpful comment
@cver can you share a link to the complete app? It would be much easier to give feedback if we have the context of the whole application.
At first glance, it's unclear what the responsibilities of each of the blocs is and it seems like a fairly complex setup. I'm wondering if the bloc(s) can be simplified but would need to see the whole application to give better recommendations.
There are also little improvements you can make like rather than having to cast state you can create a
currentState.Before:
After:
Hope that helps 馃憤