State is data that changes over the lifecycle of the app.
There is (almost) always state in our applications therefore we are (almost) always managing state whether we know it or not.
(setState)
class _HomePageState extends State<HomePage> {
int _counter = 0;
void _increment() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text('You have pushed the button this many times:'),
Text('$_counter'),
RaisedButton(
onPressed: _increment,
child: Text('Increment'),
),
],
);
}
}
(Sharing State)
(Sharing State Continued...)
(InheritedWidget)
class InheritedCounter extends InheritedWidget {
final Map _counter = { 'val': 0 };
final Widget child;
InheritedCounter({ this.child }) : super(child: child);
increment() {
_counter['val']++;
}
get counter => _counter['val'];
@override
bool updateShouldNotify(InheritedCounter oldWidget) => true;
static InheritedCounter of(BuildContext context) =>
context.inheritFromWidgetOfExactType(InheritedCounter);
}
(InheritedWidget Continued...)
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) => InheritedCounter(child: HomePage());
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
int counter = InheritedCounter.of(context).counter;
Function increment = InheritedCounter.of(context).increment;
return Column(
children: <Widget>[
Text('You have pushed the button this many times:'),
Text('$counter'),
RaisedButton(
onPressed: () => setState(() => increment()),
child: Text('Increment'),
),
],
);
},
);
}
}
(Sharing State Revisited...)
(RxDart)
BLoC stands for Business Logic Component
Business Logic should:
(RxDart Continued...)
(Implementation)
class CounterBloc {
int _counter = 0;
BehaviorSubject<int> _counterStateSubject;
PublishSubject<void> _incrementEventSubject;
Stream<int> get counter => _counterStateSubject.stream;
Sink<void> get increment => _incrementEventSubject.sink;
StreamSubscription<void> _incrementEventSubscription;
CounterBloc() {
_counterStateSubject = BehaviorSubject<int>.seeded(_counter);
_incrementEventSubject = PublishSubject<void>();
_incrementEventSubscription =
_incrementEventSubject.listen(_handleIncrement);
}
void dispose() {
_incrementEventSubscription.cancel();
_incrementEventSubject.close();
_counterStateSubject.close();
}
void _handleIncrement(_) {
_counterStateSubject.add(++_counter);
}
}
(Implementation Continued...)
class _HomePageState extends State<HomePage> {
final CounterBloc _counterBloc = CounterBloc();
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text('You have pushed the button this many times:'),
StreamBuilder<int>(
stream: _counterBloc.counter,
initialData: 0,
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
return Text('${snapshot.data}');
},
),
RaisedButton(
onPressed: () => _counterBloc.increment.add(null);
child: Text('Increment'),
),
],
);
}
@override
void dispose() {
_counterBloc.dispose();
super.dispose();
}
}
A predictable state management library that helps implement the BLoC design pattern.
(Core Concepts)
(RxDart Flashback...)
(in action)
enum CounterEvent { increment }
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc() : super(0);
@override
Stream<int> mapEventToState(CounterEvent event) async* {
switch (event) {
case CounterEvent.increment:
yield state + 1;
break;
}
}
}
(in action continued...)
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text('You have pushed the button this many times:'),
BlocBuilder<CounterBloc, int>(
builder: (BuildContext context, int counter) {
return Text('$counter'),
},
),
RaisedButton(
onPressed: () => BlocProvider.of<CounterBloc>(context).add(CounterEvent.increment),
child: Text('Increment'),
),
],
);
}
}
(BlocProvider)
BlocProvider is a Flutter widget which provides a bloc to its children via BlocProvider.of<T>(context). It is used as a DI widget so that a single instance of a bloc can be provided to multiple widgets within a subtree.
BlocProvider<BlocA>(
create: (context) => BlocA(),
child: ChildA(),
);
// without extensions:
BlocProvider.of<BlocA>(context)
// with extensions:
context.bloc<BlocA>()
then from ChildA we can retrieve BlocA via:
(transitions)
Transitions occur when an Event is dispatched after mapEventToState has been called but before the Bloc's state has been updated. A Transition consists of the currentState, the event which was dispatched, and the nextState.
BlocSupervisor oversees Blocs and delegates to BlocDelegate.
BlocDelegate handles events from all Blocs which are delegated by the BlocSupervisor. Can be used to intercept all Bloc Transitions and all Bloc errors. It is a great way to handle logging/analytics as well as error handling universally.
(BlocObserver)
class MyBlocObserver extends BlocObserver {
@override
void onError(Object error, StackTrace stacktrace) {
super.onError(error, stacktrace);
print(error);
}
@override
void onTransition(Transition transition) {
super.onTransition(transition);
print(transition);
}
@override
void onEvent(Object event) {
super.onEvent(event);
print(event);
}
}
void main() {
Bloc.observer = MyBlocObserver();
runApp(MyApp());
}
(Tooling)