Triggering initial event in BLoC
Your discomfort really has reason - no event should be fired from build()
method (build() could be fired as many times as Flutter framework needs)
Our case is to fire initial event on Bloc creation
Possibilities overview
- case with inserting Bloc with BlocProvider - this is preferred way
create:
callback is fired only once when BlocProvider is mounted & BlocProvider would close() bloc when BlocProvider is unmounted
class ExampleScreenState extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: BlocProvider( create: (context) => ExampleBloc()..add(Fetch()), // <-- first event, child: BlocBuilder<ExampleBloc, ExampleState>( builder: (BuildContext context, state) { ... }, ), ), ); } }
- case when you create Bloc in
State
of Statefull widget
class _ExampleScreenStateState extends State<ExampleScreenState> { ExampleBloc _exampleBloc; @override void initState() { super.initState(); _exampleBloc = ExampleBloc(); _exampleBloc.add(Fetch()); // or use cascade notation // _exampleBloc = ExampleBloc()..add(Fetch()); } @override void dispose() { super.dispose(); _exampleBloc.close(); // do not forget to close, prefer use BlocProvider - it would handle it for you } @override Widget build(BuildContext context) { return Scaffold( body: BlocBuilder<ExampleBloc, ExampleState>( bloc: _exampleBloc, builder: (BuildContext context, state) { ... }, ), ); }}
- add first event on Bloc instance creation - this way has drawbacks when testing because first event is implicit
class ExampleBloc extends Bloc<ExampleEvent, ExampleState> { ... ExampleBloc() { add(Fetch()); }}// insert it to widget tree with BlocProvider or create in StateBlocProvider( create: (_) => ExampleBloc(), ...// or in Stateclass _ExampleScreenStateState extends State<ExampleScreenState> { final _exampleBloc = ExampleBloc(); ...
PS feel free to reach me in comments
Sergey Salnikov has a great answer. I think I can add another suggestion however.
In my main.dart file I am using a MultiBlocProvider to create all my blocs for use further down the tree. Like so
Widget build(BuildContext context) { return MultiBlocProvider( providers: <BlocProvider<dynamic>>[ BlocProvider<OneBloc>(create: (_) => OneBloc()), BlocProvider<TwoBloc>(create: (_) => TwoBloc()), ], child: MaterialApp( // Rest of your app )
Then when I need to call an event when I load a page, in this case I wanted to fetch some data depending on a list tile selected, and I needed more options than FutureBuilder can provide me, I simple used initState(); and called the bloc provider and added an event.
class _ExampleScreenState extends State<ExampleScreen> { @override void initState() { super.initState(); BlocProvider.of<OneBloc>(context) .add(FetchData); }
It works because the bloc has already been provided from the root widget.