Closed vt-dev0 closed 3 years ago
Hi @vitaliy-t 🖐
To update the state in the bloc, you should use yield
instead of emit
.
Here you can see
Hi @vitaliy-t raised_hand_with_fingers_splayed To update the state in the bloc, you should use
yield
instead ofemit
. Here you can see
Hey. Thank you very much for your response.
I updated my TasksBloc
and replaced my emit
uses with yield
. The code looks as following now:
TasksBloc(this.taskType) : super(TasksInitialState()) {
add(TasksLoadEvent(taskType));
}
final int taskType;
final HomeRepository _homeRepository = HomeRepository();
@override
Stream<TasksState> mapEventToState(TasksEvent event) async* {
if (event is TasksLoadEvent) {
log('tasks load event');
yield TasksLoadingState();
final DataState _dataState = await _homeRepository.getTasks(event.taskType);
if (_dataState is Success) {
yield TasksSuccessState(Task.parseListOfTasks(_dataState.data));
} else if (_dataState is Failure) {
yield TasksErrorState(errorMessage: _dataState.message);
} else if (_dataState is Unauthenticated) {
yield TasksUnauthenticatedState();
} else if (_dataState is NetworkError) {
yield TasksErrorState(errorMessage: 'error_network_Body'.tr(namedArgs: {'details': _dataState.message}));
} else if (_dataState is UnknownError) {
yield TasksErrorState(errorMessage: 'error_unexpected_Body'.tr(namedArgs: {'details': _dataState.message}));
} else {
yield TasksErrorState();
}
}
}
Unfortunately this did not solve my problem and the same issue still persists.
I created a sample that has this exact error. Here is the link:
https://github.com/vitaliy-t/flutter-bloc-builder-issue-sample
@vitaliy-t I took a look at it looks like your models/states aren't using Equatable properly. For example https://github.com/vitaliy-t/flutter-bloc-builder-issue-sample/blob/74e3fbe6b4abf5508c35893b9a82d512f7a5d7dd/lib/homescreen/model/task/Task.dart#L52 is missing a bunch of props. More information can be found at https://bloclibrary.dev/#/faqs?id=state-not-updating. Let me know if that helps 👍
@vitaliy-t I took a look at it looks like your models/states aren't using Equatable properly. For example https://github.com/vitaliy-t/flutter-bloc-builder-issue-sample/blob/74e3fbe6b4abf5508c35893b9a82d512f7a5d7dd/lib/homescreen/model/task/Task.dart#L52 is missing a bunch of props. More information can be found at https://bloclibrary.dev/#/faqs?id=state-not-updating. Let me know if that helps +1
Thank you very much for your response.
To simplify things a bit further, I removed all fields from Task
model and left only 2 that are inside Equatable
property list.
I also manually ensured (for testing purposes) that all the items compared are different to completely eliminate that as an issue.
Also, I tried few things on my own. As you will be able to see in this code: https://github.com/vitaliy-t/flutter-bloc-builder-issue-sample/blob/master/lib/homescreen/view/HomeScreen.dart To keep track of how things are going in terms of app flow I added logging.
So inside HomeScreen
we have 2 events:
DrawerNavigationState
is received, we print it out to console to see what we got.if
statement.That covers my navigation issues which there seem to be none.
Then, I added another log to TasksBloc
, the one that is responsible for yielding TasksPage
. Like this:
https://github.com/vitaliy-t/flutter-bloc-builder-issue-sample/blob/master/lib/homescreen/view/taskspage/bloc/TasksBloc.dart
This will tell us that the event was received and will soon be mapped to state. That's where something strange happens.
When I switch from ProfilePage
to TasksPage
of any type we get the following log:
[log] DrawerTasksPageState(1)
[log] returning tasks page
[log] tasks bloc event is received
Awesome.
if
statementTasksBloc
which shows us that the event was sent and received.Then, we I try and move from one type of TasksPage
to another, the following log is sent out:
[log] DrawerTasksPageState(2)
[log] returning tasks page
Which means that event never gets sent.
I tried going through this with debugger and the issue is exactly that - the TasksBloc
never gets used when switching between types of TasksPage
and only when switching from ProfilePage
.
After playing around with debugger a little bit more I figured what was the issue.
For unknown to me reason, BlocBuilder
, when navigating between different TasksPage
types instead of returning newly created instance of TasksPage
returns an old one. Since the event is called only in initState
, and the initState
is not called when working with the same instance, the UI does not update even though the new taskType
is passed.
I fixed it by adding event before returning TasksPage
which seems to do the trick, but I would like to know if perhaps there is a better solution that would involve recreating TasksPage
widgets completely instead of simply sending an event.
code looks as following:
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<DrawerBloc>(
create: (context) => DrawerBloc(),
),
BlocProvider<DrawerNavigationBloc>(
create: (context) => DrawerNavigationBloc(),
),
BlocProvider(
create: (context) => TasksBloc(),
)
],
child: Scaffold(
drawer: NavigationDrawer(),
appBar: PreferredSize(
preferredSize: const Size.fromHeight(50),
child: PlatformAppBar(),
),
body: BlocBuilder<DrawerNavigationBloc, DrawerNavigationState>(
builder: (context, state) {
log(state.toString());
if (state is DrawerTasksPageState) {
log('returning tasks page');
context.read<TasksBloc>().add(TasksLoadEvent(state.taskType));
return new TasksPage(state.taskType);
} else if (state is DrawerProfilePageState) {
log('returning profile page');
return ProfilePage();
} else {
log('returning empty contaienr page');
// todo -> make this more meaningful
return Container();
}
},
),
),
);
}
}
If you want to recreate the TasksPage you could use a UniqueKey()
:
TestsPage(key: UniqueKey(), taskType: state.taskType);
But usually this is a symptom of a larger architectural issue imo. Closing for now but feel free to join the bloc discord if you want to continue the discussion 👍
Hello. While trying to find answer for my question on google I saw few people posting here for some advice, hence why I am posting here as well.
Here is the main idea of my current implementation:
HomeScreen
will containNavigationDrawer
.NavigationDrawer
will use 2 Blocs:HomeScreen
(DrawerNavigationBloc)DrawerNavigationBloc
can emit eitherDrawerProfilePageState
orDrawerTasksPageState
which will build either page correspondingly.There is no issue when it comes to building different pages (Profile or Tasks), however, that is not all. TasksPage has to have a parameter of
int taskType
passed to it in order to fetch corresponding list of Tasks from the server which will then be displayed on screen.That's where the issue currently lies. Whenever I load one
TasksPage
with anytaskType
and then simply pick another inNavDrawer
the UI is not being rebuilt, in fact, there isn't even any event being triggered inTasksBloc
.I am not really sure what exactly is the problem here, but maybe it has something to do with
BlocProvider
inHomeScreen
.Code is below:
HomeScreen
:TasksPage
TasksBloc
NavigationDrawer
DrawerBloc
DrawerNavigationBloc
============================================== Please help me out here.