Bloc: State Changes are not Correctly Emitted

Created on 7 May 2020  ·  2Comments  ·  Source: felangel/bloc

Describe the bug
I have recently come across the following issue in my project. With this configuration of bloc, state and event classes state changes occur in the BLoC but are not properly emitted. If I add an event to the BLoC, it is registered and the state is changed as expected, but the new state is not emitted.

The UI

````dart
void main() {
runApp(MyApp());
}

class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}

class _MyAppState extends State {
final TestBloc testBloc = TestBloc();

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'State Demo',
home: BlocBuilder(
bloc: testBloc,
builder: (context, state) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
(state as IdleState).entries.toString(),
),
SizedBox(height: 20),
FlatButton(
child: Text("Add Entry"),
onPressed: () => testBloc.add(
AddEntryEvent(
Random().nextInt(100000),
Random().nextInt(100000),
),
),
)
],
),
),
);
}),
);
}

@override
void dispose() {
testBloc.close();
super.dispose();
}
}
````

The BLoC

````dart
class TestBloc extends Bloc {
@override
TestState get initialState => IdleState(HashMap());

@override
Stream mapEventToState(TestEvent event) async* {
if(event is AddEntryEvent && state is IdleState){
HashMap newEntries = (state as IdleState).entries;
newEntries[event.index] = event.entry;

  yield IdleState(newEntries);
}

}
}
````

The State

````dart
abstract class TestState extends Equatable {
const TestState();
}

class IdleState extends TestState {
final HashMap entries;

IdleState(this.entries);

@override
List get props => [entries.toString()];
}
````

The Event

````dart
abstract class TestEvent extends Equatable {
const TestEvent();
}

class AddEntryEvent extends TestEvent{
final int index;
final dynamic entry;

AddEntryEvent(this.index, this.entry);

@override
List get props => [index, entry];

}
````

Expected behavior
I would expect the UI to update after adding the AddEntryEvent to the TestBloc.

Screenshots
The UI only updates after a hot-reload.

image

Logs
No issues found! (ran in 3.1s)

````
[√] Flutter (Channel dev, 1.18.0-10.0.pre, on Microsoft Windows [Version 10.0.18363.815], locale en-GB)
• Flutter version 1.18.0-10.0.pre at C:\Users\sebas\Documents\Coding\flutter
• Framework revision 9b7b9d795e (2 days ago), 2020-05-05 12:09:51 -0700
• Engine revision 33d2367950
• Dart version 2.9.0 (build 2.9.0-5.0.dev 9c94f08410)

[!] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
• Android SDK at C:\Users\sebas\Documents\SDKs
• Platform android-29, build-tools 29.0.2
• ANDROID_HOME = C:\Users\sebas\Documents\SDKs
• Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
• Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b03)
X Android license status unknown.
Try re-installing or updating your Android SDK Manager.
See https://developer.android.com/studio/#downloads or visit visit
https://flutter.dev/docs/get-started/install/windows#android-setup for detailed instructions.

[!] Android Studio (version 3.5)
• Android Studio at C:\Program Files\Android\Android Studio
X Flutter plugin not installed; this adds Flutter specific functionality.
X Dart plugin not installed; this adds Dart specific functionality.
• Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b03)

[!] IntelliJ IDEA Community Edition (version 2019.3)
• IntelliJ at C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.3.4
X Flutter plugin not installed; this adds Flutter specific functionality.
X Dart plugin not installed; this adds Dart specific functionality.
• For information about installing plugins, see
https://flutter.dev/intellij-setup/#installing-the-plugins

[√] VS Code (version 1.44.2)
• VS Code at C:\Users\sebas\AppData\Local\Programs\Microsoft VS Code
• Flutter extension version 3.10.1

[√] Connected device (1 available)
• Android SDK built for x86 • emulator-5554 • android-x86 • Android 10 (API 29) (emulator)

! Doctor found issues in 3 categories.`
````

question

Most helpful comment

Thank you @RollyPeres 😁,

yes, copying the state using .from() did the trick.

Thanks a lot for the help and the clear explanation of the problem.

All 2 comments

Hi @Fasust ! ✌

Looks like you're mutating the existing state thus your new states are not exiting the bloc.
To avoid this please use the following approach:

 HashMap<int, dynamic> newEntries = (state as IdleState).entries;
      yield IdleState(
        HashMap<int, dynamic>.from(newEntries)
          ..addAll({event.index: event.entry}),
      );

I've also noticed in your IdleState your props override utilizes .toString() on entries, which is not necessary. Although value equality will work, you could simply have it like:

@override
  List<Object> get props => [entries];

Hope that gives you a better understanding of the issue you're facing 👍

Thank you @RollyPeres 😁,

yes, copying the state using .from() did the trick.

Thanks a lot for the help and the clear explanation of the problem.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rsnider19 picture rsnider19  ·  3Comments

komapeb picture komapeb  ·  3Comments

1AlexFix1 picture 1AlexFix1  ·  3Comments

wheel1992 picture wheel1992  ·  3Comments

craiglabenz picture craiglabenz  ·  3Comments