Issue
I have implement a this simple bloc with the flutter_bloc package:
class MainBloc extends Bloc<MainEvent, MainState> {
@override
MainState get initialState => Init();
@override
Stream<MainState> mapEventToState(MainEvent event) async* {
if (event is Event) {
yield* _mapEventToState();
}
}
Stream<MainState> _mapEventToState() async* {
final loadState = Load();
print("Yield state: $loadState");
yield loadState;
await sleep(Duration(seconds: 1));
final initState = Init();
print("Yield state: $initState");
yield initState;
}
}
with this event:
abstract class MainEvent {}
class Event extends MainEvent {}
and this state:
abstract class MainState {}
class Load extends MainState {}
class Init extends MainState {}
My UI looks like this:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
home: BlocProvider(
create: (context) => MainBloc(),
child: Scaffold(
appBar: AppBar(),
body: BlocBuilder<MainBloc, MainState>(
builder: (context, state) {
print("Build state: $state");
if (state is Init) {
return MaterialButton(
onPressed: () => BlocProvider.of<MainBloc>(context).add(Event()),
child: Text("Press"),
);
} else {
return Text("Loading");
}
},
),
),
),
);
}
}
Unfortunately if I add my Event with the MaterialButton, the Load() state gets ignored. The UI doesn’t rebuild the Load state. The output is the following:
I/flutter ( 1955): Yield state: Instance of ‘Load’
I/flutter ( 1955): Yield state: Instance of ‘Init’
I/flutter ( 1955): Build state: Instance of ‘Init’
Solution
It’s because you’re using await sleep(Duration(seconds: 1));
(the await
statement is of no use here, it is a synchronous function).
The sleep
function pauses the execution of the main thread, meaning it will also block the rebuilding of the Main page of your app, and so it doesn’t get the chance to rebuild because of the new Load
state. Once freezing the app is over, it immediately gets a new state, so that’s why you will never see the loading page.
Use await Future.delayed(Duration(seconds: 1));
instead, which pauses the execution of the rest of the _mapEventToState
function but doesn’t block the main thread and so your MainPage gets a rebuild.
Answered By – JJuice
Answer Checked By – David Goodson (FlutterFixes Volunteer)