Two days ago, I just learned the basic management of Bloc status. UI updates data through setState(). Today, I have learned more about the advanced FlutterBloc status management.

FlutterBloc

FlutterBloc can facilitate the implementation of Bloc and is mainly constructed for co-use with Bloc. You also need to know a few concepts in advance; The side dish continues to expand the Demo in the previous section by adding increasing and decreasing Number.

BlocBuilder

BlocBuilder is used to build widgets to respond to new states. It is more convenient than StreamBuilder. Instead of setState() as used in the previous side dish section;

const BlocBuilder({
    Key key,
    @required this.builder,
    B bloc,
    BlocBuilderCondition<S> condition,
})
Copy the code

Analysis of the source code shows that the Builder is used for widgets of the corresponding status, but bloc is only used for the bloc that is currently provided in a single Widget and cannot be accessed through the parent BlocProvider or the current level. Condition is an optional, overly fine-grained Widget that takes two parameters, the previous state and the current state, and returns a Boolean value. If the Widget is true, it will be rebuilt by updating the state. If it is false, it will not be rebuilt.

@override Widget build(BuildContext context) { return BlocBuilder<NumberBloc, int>( bloc: _numBloc, condition: (previousState, state) { print('BlocPage.condition->$previousState==$state'); return state <= 30 ? true : false; }, builder: (context, count) { return Scaffold( appBar: AppBar(title: Text('Bloc Page')), body: Center( child: The Column (mainAxisAlignment: mainAxisAlignment center, children: < widgets > [Text (' when Number > 30, don't make changes', style: TextStyle(fontSize: 20.0, color: color.blue), SizedBox(height: 20.0), Text(' current Number = ${_numBloc. State}', style: TextStyle(fontSize: 20.0, color: color.blue))]), Column(crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[ FloatingActionButton( heroTag: 'addTag', child: Icon(Icons.add), onPressed: () => _numBloc.add(NumberEvent.addEvent)), SizedBox(height: 20.0), FloatingActionButton(heroTag: 'removeTag', Child: Icon(icon.remove), onPressed: () => _numBloc.add(NumberEvent.removeEvent)) ])); }); }Copy the code

BlocProvider

BlocProvider is the supplier of Bloc, creates Bloc and supplies it to its child control tree.

BlocProvider({
    Key key,
    @required Create<T> create,
    Widget child,
    bool lazy,
})
Copy the code

BlocProvider creates Bloc through Create. A Widget set with Child to respond to changes in state; Lazy indicates whether the creation is delayed. The default value is true.

class _BlocPageState extends State<BlocPage> { @override Widget build(BuildContext context) { return BlocProvider( create: (BuildContext context) => NumberBloc(), child: BlocBuilder<NumberBloc, int>( condition: (previousState, state) { print('BlocPage.condition->$previousState==$state'); return state <= 30 ? true : false; }, builder: (context, count) { return Scaffold( appBar: AppBar(title: Text('Bloc Page')), body: Center( child: The Column (mainAxisAlignment: mainAxisAlignment center, children: < widgets > [Text (' when Number > 30, don't make changes', style: TextStyle(fontSize: 20.0, color: color.blue)), SizedBox(height: Text(' current Number = ${BlocProvider. Of <NumberBloc>(context).state}', style: TextStyle(fontSize: 20.0, color: Colors.blue)) ])), floatingActionButton: Column( crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[ FloatingActionButton( heroTag: 'addTag', child: Icon(Icons.add), onPressed: () => BlocProvider.of<NumberBloc>(context).add(NumberEvent.addEvent)), SizedBox(height: 20.0), FloatingActionButton(heroTag: 'removeTag', Child: Icon(icon.remove), onPressed: () => BlocProvider.of<NumberBloc>(context).add(NumberEvent.removeEvent)) ])); })); }}Copy the code

BlocListener

BlocListener is similar to BlocBuilder application; The listener is used to listen to the state change, and the corresponding business processing can be made here.

class BlocListener<B extends Bloc<dynamic, S>, S> extends BlocListenerBase<B, S> with BlocListenerSingleChildWidget {
  final Widget child;
  const BlocListener({
    Key key,
    @required BlocWidgetListener<S> listener,
    B bloc,
    BlocListenerCondition<S> condition,
    this.child,
  })
}
Copy the code

Simple analysis source code can be obtained:

  1. Child provides a Widget for BlocListener to respond to state changes;
  2. Bloc is used the same way as BlocBuilder. If the bloc parameter is omitted, BlocListener will automatically find the BuildContext using BlocProvider and the current function.
  3. Condition is an optional excessively fine-grained condition that contains two parameters, the previous state and the current state. The return value is of type Boolean. If true, the listener will be listened on, and if false, the listener will be filtered. The filter is independent of the condition filter in BlocBuilder;
  4. The listener is called each time the state changes. It includes the context and the current state.
@override Widget build(BuildContext context) { return BlocListener<NumberBloc, int>( bloc: _numBloc, listener: (context, count) { print('BlocPage.listene->$count'); }, condition: (previousState, state) { print('BlocPage.BlocListener.condition->$previousState==$state'); return state <= 20 ? true : false; }, child: BlocBuilder<NumberBloc, int>( bloc: _numBloc, condition: (previousState, state) { print('BlocPage.condition->$previousState==$state'); return state <= 30 ? true : false; }, builder: (context, count) { return Scaffold( appBar: AppBar(title: Text('Bloc Page')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[Text(' when Number > 20, BlocListener filters listener listener, independent of the status of BlocBuilder filters ', style: TextStyle(fontSize: 20.0, color: color.red)), SizedBox(height: 20.0), Text(' when Number > 30, Number does not change ', style: TextStyle(fontSize: 20.0, color: color.green)), SizedBox(height: 20.0), Text(' current Number = ${_numBloc. State}', style: TextStyle(fontSize: 20.0, color: color.blue))]), floatingActionButton: Column(crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[ FloatingActionButton( heroTag: 'addTag', child: Icon(Icons.add), onPressed: () => _numBloc.add(NumberEvent.addEvent)), SizedBox(height: 20.0), FloatingActionButton(heroTag: 'removeTag', Child: Icon(icon.remove), onPressed: () => _numBloc.add(NumberEvent.removeEvent)) ])); })); }Copy the code

TestCode

There are some small problems in the test process of the side dishes, just briefly record, in case you forget;

Q1: There are multiple heroes that share the same tag within a subtree.

When expanding the Demo in the last section, a black screen will appear when you click to enter the page, prompting the following error;

A1: Add the heroTag distinction in the FloatingActionButton

We can’t use the same heroTag on the same Page as the Hero Animation button. We can’t use the same heroTag on the same Page as the Hero tag in the FloatingActionButton.

floatingActionButton: Column(
    crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
    children: <Widget>[
      FloatingActionButton(
          heroTag: 'addTag', child: Icon(Icons.add),
          onPressed: () => _numBloc.add(NumberEvent.addEvent)),
      FloatingActionButton(
          heroTag: 'removeTag', child: Icon(Icons.remove),
          onPressed: () => _numBloc.add(NumberEvent.removeEvent))
    ]));
Copy the code

Q2: BlocProvider.of() called with a context that does not contain a Bloc of type …

[Bug Mc-10827] – Trying to get Bloc in blocprovider.of (context) at first

A2: Create it outside of Build () or as follows and suggest pairing it with BlocBuilder

// Create NumberBloc _numBloc outside the build() method; @override void initState() { super.initState(); _numBloc = NumberBloc(); Create BlocProvider(create: (BuildContext context) => NumberBloc(), child:....) ;Copy the code

Many advanced uses of FlutterBloc have not been involved in the side dish. In the next section, I will try to use multiple Bloc together. I don’t have a good understanding of all aspects.

Source: Young Monk Atze