flutter_bloc 4.0.0-dev.4 flutter_bloc: ^4.0.0-dev.4 copied to clipboard
Flutter Widgets that make it easy to implement the BLoC (Business Logic Component) design pattern. Built to be used with the bloc state management package.
A Flutter package that helps implement the BLoC pattern.
This package is built to work with bloc.
Bloc Widgets #
BlocBuilder is a Flutter widget which requires a Bloc
and a builder
function. BlocBuilder
handles building the widget in response to new states. BlocBuilder
is very similar to StreamBuilder
but has a more simple API to reduce the amount of boilerplate code needed. The builder
function will potentially be called many times and should be a pure function that returns a widget in response to the state.
See BlocListener
if you want to "do" anything in response to state changes such as navigation, showing a dialog, etc...
If the bloc parameter is omitted, BlocBuilder
will automatically perform a lookup using BlocProvider
and the current BuildContext
.
BlocBuilder<BlocA, BlocAState>(
builder: (context, state) {
// return widget here based on BlocA's state
}
)
Only specify the bloc if you wish to provide a bloc that will be scoped to a single widget and isn't accessible via a parent BlocProvider
and the current BuildContext
.
BlocBuilder<BlocA, BlocAState>(
bloc: blocA, // provide the local bloc instance
builder: (context, state) {
// return widget here based on BlocA's state
}
)
If you want fine-grained control over when the builder function is called you can provide an optional condition
to BlocBuilder
. The condition
takes the previous bloc state and current bloc state and returns a boolean. If condition
returns true, builder
will be called with state
and the widget will rebuild. If condition
returns false, builder
will not be called with state
and no rebuild will occur.
BlocBuilder<BlocA, BlocAState>(
condition: (previousState, state) {
// return true/false to determine whether or not
// to rebuild the widget with state
},
builder: (context, state) {
// return widget here based on BlocA's state
}
)
BlocProvider is a Flutter widget which provides a bloc to its children via BlocProvider.of<T>(context)
. It is used as a dependency injection (DI) widget so that a single instance of a bloc can be provided to multiple widgets within a subtree.
In most cases, BlocProvider
should be used to create new blocs
which will be made available to the rest of the subtree. In this case, since BlocProvider
is responsible for creating the bloc, it will automatically handle closing the bloc.
BlocProvider(
create: (BuildContext context) => BlocA(),
child: ChildA(),
);
In some cases, BlocProvider
can be used to provide an existing bloc to a new portion of the widget tree. This will be most commonly used when an existing bloc
needs to be made available to a new route. In this case, BlocProvider
will not automatically close the bloc since it did not create it.
BlocProvider.value(
value: BlocProvider.of<BlocA>(context),
child: ScreenA(),
);
then from either ChildA
, or ScreenA
we can retrieve BlocA
with:
// with extensions
context.bloc<BlocA>();
// without extensions
BlocProvider.of<BlocA>(context)
MultiBlocProvider is a Flutter widget that merges multiple BlocProvider
widgets into one.
MultiBlocProvider
improves the readability and eliminates the need to nest multiple BlocProviders
.
By using MultiBlocProvider
we can go from:
BlocProvider<BlocA>(
create: (BuildContext context) => BlocA(),
child: BlocProvider<BlocB>(
create: (BuildContext context) => BlocB(),
child: BlocProvider<BlocC>(
create: (BuildContext context) => BlocC(),
child: ChildA(),
)
)
)
to:
MultiBlocProvider(
providers: [
BlocProvider<BlocA>(
create: (BuildContext context) => BlocA(),
),
BlocProvider<BlocB>(
create: (BuildContext context) => BlocB(),
),
BlocProvider<BlocC>(
create: (BuildContext context) => BlocC(),
),
],
child: ChildA(),
)
BlocListener is a Flutter widget which takes a BlocWidgetListener
and an optional Bloc
and invokes the listener
in response to state changes in the bloc. It should be used for functionality that needs to occur once per state change such as navigation, showing a SnackBar
, showing a Dialog
, etc...
listener
is only called once for each state change (NOT including initialState
) unlike builder
in BlocBuilder
and is a void
function.
If the bloc parameter is omitted, BlocListener
will automatically perform a lookup using BlocProvider
and the current BuildContext
.
BlocListener<BlocA, BlocAState>(
listener: (context, state) {
// do stuff here based on BlocA's state
},
child: Container(),
)
Only specify the bloc if you wish to provide a bloc that is otherwise not accessible via BlocProvider
and the current BuildContext
.
BlocListener<BlocA, BlocAState>(
bloc: blocA,
listener: (context, state) {
// do stuff here based on BlocA's state
}
)
If you want fine-grained control over when the listener function is called you can provide an optional condition
to BlocListener
. The condition
takes the previous bloc state and current bloc state and returns a boolean. If condition
returns true, listener
will be called with state
. If condition
returns false, listener
will not be called with state
.
BlocListener<BlocA, BlocAState>(
condition: (previousState, state) {
// return true/false to determine whether or not
// to call listener with state
},
listener: (context, state) {
// do stuff here based on BlocA's state
},
child: Container(),
)
MultiBlocListener is a Flutter widget that merges multiple BlocListener
widgets into one.
MultiBlocListener
improves the readability and eliminates the need to nest multiple BlocListeners
.
By using MultiBlocListener
we can go from:
BlocListener<BlocA, BlocAState>(
listener: (context, state) {},
child: BlocListener<BlocB, BlocBState>(
listener: (context, state) {},
child: BlocListener<BlocC, BlocCState>(
listener: (context, state) {},
child: ChildA(),
),
),
)
to:
MultiBlocListener(
listeners: [
BlocListener<BlocA, BlocAState>(
listener: (context, state) {},
),
BlocListener<BlocB, BlocBState>(
listener: (context, state) {},
),
BlocListener<BlocC, BlocCState>(
listener: (context, state) {},
),
],
child: ChildA(),
)
BlocConsumer exposes a builder
and listener
in order react to new states. BlocConsumer
is analogous to a nested BlocListener
and BlocBuilder
but reduces the amount of boilerplate needed. BlocConsumer
should only be used when it is necessary to both rebuild UI and execute other reactions to state changes in the bloc
. BlocConsumer
takes a required BlocWidgetBuilder
and BlocWidgetListener
and an optional bloc
, BlocBuilderCondition
, and BlocListenerCondition
.
If the bloc
parameter is omitted, BlocConsumer
will automatically perform a lookup using
BlocProvider
and the current BuildContext
.
BlocConsumer<BlocA, BlocAState>(
listener: (context, state) {
// do stuff here based on BlocA's state
},
builder: (context, state) {
// return widget here based on BlocA's state
}
)
An optional listenWhen
and buildWhen
can be implemented for more granular control over when listener
and builder
are called. The listenWhen
and buildWhen
will be invoked on each bloc
state
change. They each take the previous state
and current state
and must return a bool
which determines whether or not the builder
and/or listener
function will be invoked. The previous state
will be initialized to the state
of the bloc
when the BlocConsumer
is initialized. listenWhen
and buildWhen
are optional and if they aren't implemented, they will default to true
.
BlocConsumer<BlocA, BlocAState>(
listenWhen: (previous, current) {
// return true/false to determine whether or not
// to invoke listener with state
},
listener: (context, state) {
// do stuff here based on BlocA's state
},
buildWhen: (previous, current) {
// return true/false to determine whether or not
// to rebuild the widget with state
},
builder: (context, state) {
// return widget here based on BlocA's state
}
)
RepositoryProvider is a Flutter widget which provides a repository to its children via RepositoryProvider.of<T>(context)
. It is used as a dependency injection (DI) widget so that a single instance of a repository can be provided to multiple widgets within a subtree. BlocProvider
should be used to provide blocs whereas RepositoryProvider
should only be used for repositories.
RepositoryProvider(
create: (context) => RepositoryA(),
child: ChildA(),
);
then from ChildA
we can retrieve the Repository
instance with:
// with extensions
context.repository<RepositoryA>();
// without extensions
RepositoryProvider.of<RepositoryA>(context)
MultiRepositoryProvider is a Flutter widget that merges multiple RepositoryProvider
widgets into one.
MultiRepositoryProvider
improves the readability and eliminates the need to nest multiple RepositoryProvider
.
By using MultiRepositoryProvider
we can go from:
RepositoryProvider<RepositoryA>(
create: (context) => RepositoryA(),
child: RepositoryProvider<RepositoryB>(
create: (context) => RepositoryB(),
child: RepositoryProvider<RepositoryC>(
create: (context) => RepositoryC(),
child: ChildA(),
)
)
)
to:
MultiRepositoryProvider(
providers: [
RepositoryProvider<RepositoryA>(
create: (context) => RepositoryA(),
),
RepositoryProvider<RepositoryB>(
create: (context) => RepositoryB(),
),
RepositoryProvider<RepositoryC>(
create: (context) => RepositoryC(),
),
],
child: ChildA(),
)
Usage #
Lets take a look at how to use BlocBuilder
to hook up a CounterPage
widget to a CounterBloc
.
counter_bloc.dart #
enum CounterEvent { increment, decrement }
class CounterBloc extends Bloc<CounterEvent, int> {
@override
int get initialState => 0;
@override
Stream<int> mapEventToState(CounterEvent event) async* {
switch (event) {
case CounterEvent.decrement:
yield state - 1;
break;
case CounterEvent.increment:
yield state + 1;
break;
}
}
}
counter_page.dart #
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final CounterBloc counterBloc = context.bloc<CounterBloc>();
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: BlocBuilder<CounterBloc, int>(
builder: (context, count) {
return Center(
child: Text(
'$count',
style: TextStyle(fontSize: 24.0),
),
);
},
),
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(vertical: 5.0),
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
counterBloc.add(CounterEvent.increment);
},
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 5.0),
child: FloatingActionButton(
child: Icon(Icons.remove),
onPressed: () {
counterBloc.add(CounterEvent.decrement);
},
),
),
],
),
);
}
}
At this point we have successfully separated our presentational layer from our business logic layer. Notice that the CounterPage
widget knows nothing about what happens when a user taps the buttons. The widget simply tells the CounterBloc
that the user has pressed either the increment or decrement button.
Gallery #
Examples #
- Counter - an example of how to create a
CounterBloc
to implement the classic Flutter Counter app. - Form Validation - an example of how to use the
bloc
andflutter_bloc
packages to implement form validation. - Bloc with Stream - an example of how to hook up a
bloc
to aStream
and update the UI in response to data from theStream
. - Infinite List - an example of how to use the
bloc
andflutter_bloc
packages to implement an infinite scrolling list. - Login Flow - an example of how to use the
bloc
andflutter_bloc
packages to implement a Login Flow. - Firebase Login - an example of how to use the
bloc
andflutter_bloc
packages to implement login via Firebase. - Github Search - an example of how to create a Github Search Application using the
bloc
andflutter_bloc
packages. - Weather - an example of how to create a Weather Application using the
bloc
andflutter_bloc
packages. The app uses aRefreshIndicator
to implement "pull-to-refresh" as well as dynamic theming. - Todos - an example of how to create a Todos Application using the
bloc
andflutter_bloc
packages. - Timer - an example of how to create a Timer using the
bloc
andflutter_bloc
packages. - Firestore Todos - an example of how to create a Todos Application using the
bloc
andflutter_bloc
packages that integrates with cloud firestore. - Shopping Cart - an example of how to create a Shopping Cart Application using the
bloc
andflutter_bloc
packages based on flutter samples. - Dynamic Form - an example of how to use the
bloc
andflutter_bloc
packages to implement a dynamic form which pulls data from a repository.
Dart Versions #
- Dart 2: >= 2.6.0