preface

A little mixed feeling after reading the Bloc source code…

Say something positive…

For those of you who have used Bloc, I’m sure you can tell that the Bloc framework has a very clear division of the development page, and it forces two development modes

  • Bloc Mode: This mode is divided into four tiers

    • Bloc: Logic layer
    • State: Data layer
    • Event: All interaction events
    • Page view:
  • Cubit pattern: This pattern divides into three layers

    • Cubit: Logic layer
    • State: Data layer
    • Page view:

The author is proficient in the division of layers. The state layer is written directly inside the framework, and this layer must be separated separately. I feel like if I hadn’t been stuck in the Cthulhu code mountain of large projects, I wouldn’t have had this deep obsession

This layer of state, I think is quite necessary, because once a page maintains a lot of state, mixing state variables and logical methods can be a big maintenance headache.

To be a little critical…

  • You may often see someone in the group saying Bloc encapsulates the Provider.

    • Let me confirm here: this is true, Bloc did seal the Provider
    • However, it only uses the function of using the Provider’s neutron node to query the data of the most recent parent inheritedElement and the layout of the top Widget in parallel. The classic refresh mechanism of the Provider is completely useless.
  • I think the Bloc author might be a little confused about the Provider’s refresh mechanism

    • Even if the bloc framework in build widgets in use by one line: the Provider, of < T > (context, listen: true) or remove e.m arkNeedsNotifyDependents (), I wouldn’t say this…
    • Bloc framework. I’m very confused, did some _startListening method in the callback invoked the e.m arkNeedsNotifyDependents (), totally useless! Provider.of

      (context, listen: true) To use an inheritedElement to add child elements To verify my idea, I debug the notifyClients method on the Framework layer. When calling emit or yield to refresh, the _dependents map is always empty.
  • I’ve made a lot of fun of it above, not what do I have against Bloc

    • Bloc I also spent a lot of time and depth in the process, optimizing its usage, and writing a code generation plug-in for it, which I put some time and effort into
    • But: the code doesn’t lie, all the good and the bad are in it, and you can feel it with your heart.

Why say the mood is mixed?

When I read the Provider source code before, I had a headache. The internal logic was indeed a little complicated, but the overall process was reasonable and the refresh logic was clear. It was a feeling of full of joy! After the pain is a great satisfaction, and to the Provider skilled use of the Framework layer of various APIs, and then achieve a wonderful refresh mechanism, feel praise!

Then, as mentioned above, I did put some effort into Bloc, optimizing its use, then looked at its source code and thought back to the Provider source code I had been looking at earlier, and suddenly there was a huge drop off.

In my opinion, with such a well-known open source library, these bumps can be avoided; Maybe it’s this kind of inexplicably high expectation that makes me have this kind of gap…

By the way, the Bloc author may have deliberately left a Provider refresh mechanism in the Bloc, using this as an Easter Egg!

Felling this point a knot in one’s heart did not suddenly!

use

Here to introduce the use of the official usage has made some adjustments

To adjust the path of your mind, see: Flutter_Bloc Useful Parsing — Are you still using your Bloc?

So I’m just going to write it in this way

The plug-in

Because the official plug-in generation of writing, and after the adjustment of writing gap is a little big, and the official plug-in does not support the generation of view layer and related Settings, here I am a plug-in, improve the relevant functions

Note that the Wrap code and hint snippets follow the official plugin rules

The Wrap Widget rule is intellij_generator_plugin

The shortcut code generation rule is intellij_generator_plugin

  • Search for Flutter Bloc in Android Studio

  • Generate template code

  • Support for modifying suffixes

  • Wrap Widget (Alt + Enter) : RepositoryProvider, BlocConsumer, BlocBuilder, BlocProvider, BlocListener

  • Typing bloc generates a quick snippet of code

usage

The plug-in generates two pattern codes: Bloc and Cubit; Look at

Cubit mode

  • view
class CounterPage extends StatelessWidget { final cubit = CounterCubit(); @override Widget build(BuildContext context) { return BlocProvider( create: (BuildContext context) => cubit, child: Container(), ); }}
  • cubit
class CounterCubit extends Cubit<CounterState> {
  CounterCubit() : super(CounterState().init());
}
  • state
class CounterState { CounterState init() { return CounterState(); } CounterState clone() { return CounterState(); }}

Bloc mode

  • View: An initialization event is added by default
class CounterPage extends StatelessWidget {
  final bloc = CounterBloc();

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (BuildContext context) => bloc..add(InitEvent()),
      child: Container(),
    );
  }
}
  • bloc
class CounterBloc extends Bloc<CounterEvent, CounterState> { CounterBloc() : super(CounterState().init()); @override Stream<CounterState> mapEventToState(CounterEvent event) async* { if (event is InitEvent) { yield await init(); } } Future<CounterState> init() async { return state.clone(); }}
  • event
abstract class CounterEvent {}

class InitEvent extends CounterEvent {}
  • state
class CounterState { CounterState init() { return CounterState(); } CounterState clone() { return CounterState(); }}

conclusion

Bloc and Cubit patterns are very clear about the structure, because there are multiple layers of structure, there must be appropriate template code and files, and writing these template code every time without the help of plugins would be very difficult; Here for you to write this plug-in, if there is any BUG, trouble feedback in time ha…

I’m not going to repeat how to use it, but for some details on how to use it, see: Flutter_Bloc is being parsed – are you still wearing your Bloc?

Front knowledge

To understand the Bloc principles, you need to understand the Stream

StreamController and StreamBuilder: The combination of StreamController and StreamBuilder can also be used to refresh local widgets

  • View: Stream must be closed, so StatefulWidget will need to be used, and its Dispose callback will need to be used
class StreamPage extends StatefulWidget { const StreamPage({Key? key}) : super(key: key); @override _StreamPageState createState() => _StreamPageState(); } class _StreamPageState extends State<StreamPage> { final logic = StreamLogic(); @Override Widget Build (BuildContext Context) {return Scaffold(appBar: appBar (title: Text('Bloc-Bloc ')), body: Center( child: StreamBuilder<StreamState>( initialData: logic.state, stream: logic.stream, builder: (context, snapshot) {return Text(' ${snapshot.data! .count} ', style: TextStyle(fontSize: 30.0),); }, ), ), floatingActionButton: FloatingActionButton( onPressed: () => logic.increment(), child: Icon(Icons.add), ), ); } @override void dispose() { logic.dispose(); super.dispose(); }}
  • Logic: The Stream data source is generic and can be used directly with the underlying type, using entities here in order to extend more data later
class StreamLogic { final state = StreamState(); // Final _controller = streamController < streamState >.Broadcast (); Stream<StreamState> get stream => _controller.stream; void increment() { _controller.add(state.. count = ++state.count); } void Dispose () {// Close the stream controller and dispose of the resource _controller.close(); }}
  • state
class StreamState {
  int count = 0;
}
  • rendering

In fact, looking at the above usage, there are a few things that are quite troublesome

  • A series of objects that need to create a Stream
  • The Stream must be closed, so use StatefulWidget
  • StreamBuilder needs to write three parameters, which is cumbersome

Bloc author uses the Provider’s InheritedProvider control to resolve all pain points above

The refresh mechanism

The Bloc refresh mechanism is very simple, the Stream operation above basically clarifies its core refresh mechanism, but the Bloc author does some encapsulation, let’s take a look

The charm of BlocProvider

BlocProvider is a very important control that can be used to reduce the refresh parameters and close the Stream because it wraps an inheritedProvider within a Provider; But, but in my opinion, it is still a very attractive control

  • BlocProvider: The source code for BlocProvider is simple. Here is the source code for this class
class BlocProvider<T extends BlocBase<Object? >> extends SingleChildStatelessWidget with BlocProviderSingleChildWidget { /// {@macro bloc_provider} BlocProvider({ Key? key, required Create<T> create, this.child, this.lazy, }) : _create = create, _value = null, super(key: key, child: child); BlocProvider.value({ Key? key, required T value, this.child, }) : _value = value, _create = null, lazy = null, super(key: key, child: child); /// Widget which will have access to the [Bloc] or [Cubit]. final Widget? child; final bool? lazy; final Create<T>? _create; final T? _value; static T of<T extends BlocBase<Object? >>( BuildContext context, { bool listen = false, }) { try { return Provider.of<T>(context, listen: listen); } on ProviderNotFoundException catch (e) { if (e.valueType ! = T) rethrow; throw FlutterError( ''' BlocProvider.of() called with a context that does not contain a $T. No ancestor could be found starting from the context that was passed to BlocProvider.of<$T>(). This can happen if the context you used comes from a  widget above the BlocProvider. The context used was: $context ''', ); } } @override Widget buildWithChild(BuildContext context, Widget? child) { final value = _value; return value ! = null ? InheritedProvider<T>.value( value: value, startListening: _startListening, lazy: lazy, child: child, ) : InheritedProvider<T>( create: _create, dispose: (_, bloc) => bloc.close(), startListening: _startListening, child: child, lazy: lazy, ); } static VoidCallback _startListening( InheritedContext<BlocBase> e, BlocBase value, ) { final subscription = value.stream.listen( (dynamic _) => e.markNeedsNotifyDependents(), ); return subscription.cancel; }}
  • The difference between blocProvider and blocProvider.value

    • Blocprovider. value does not automatically close the Stream

      • So blocProvider.value should not be used on a normal single page but can be used on a global Bloc instance
    • Use BlocProvider to create Bloc or Cubit for a single page
  • Create is the externally instantiated XXXBloc and is finally passed into an InheritedProvider

    • Create is the external incoming instance of XXXBloc
    • In this instance directly introduced into InheritedProvider, which is involved in the Provider, is ultimately stored in _InheritedProviderScopeElement _startListening is the Provider of the content

      • The mechanics behind this are complex and important, so check out the other side of the Flutter Provider for more information.
    • Really the logic in _startListening is useless

      • MarkNeedsNotifyDependents this API is the author of the Provider for the Provider child Element refresh, must be the Provider of form a complete set of < T > (context, listen: True) to register the Widget
      • Involve too much logic, all in the above Provider source analysis article, interested can go to have a look
  • BlocProvider.of<T>

    • Function: You can get the incoming XXXBloc from the BlocProvider Create in the child control of the BlocProvider package
    • Note that if you use the BlocProvider parent context to get XXXBloc, it must be a child layout of the BlocProvider
    • The flip side of the Flutter Provider (Swastika + Plugins) is again in this article

      • I’m not really promoting this article. The BlocProvider part, Bloc uses too many Provider features
      • Provider article, I spent a lot of effort to analyze the principle, here, there is no need to do repeat machine

Summary: To summarize the purpose of the BlocProvider class

  1. The blocProvider may store an external incoming instance of XXXBloc, and the XXXBloc class must inherit the blocBase
  2. The instance of XXXBloc stored in the blocProvider can be obtained through the blocProvider.of

    (must be in the blocProvider or its child widgets).
  3. The instance XXXBloc acquired by the blocProvider is automatically released; The XxxBloc of the blocProvider.value named constructor instance will not be automatically released

BlocProvider implements all three of these blockhouses and basically simplifies the Stream usage pattern completely

  • graphic

Cornerstone BlocBase

Needless to say, blocBase is an important abstract class

  • BlocBase
abstract class BlocBase<State> { BlocBase(this._state) { Bloc.observer.onCreate(this); } StreamController<State>? __stateController; StreamController<State> get _stateController { return __stateController ?? = StreamController<State>.broadcast(); } State _state; bool _emitted = false; State get state => _state; Stream<State> get stream => _stateController.stream; @Deprecated('Use stream. Listen instead. Will be removed in V8.0.0 ', ) StreamSubscription<State> listen( void Function(State)? onData, { Function? onError, void Function()? onDone, bool? cancelOnError, }) { return stream.listen( onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError, ); } void emit(State state) { if (_stateController.isClosed) return; if (state == _state && _emitted) return; onChange(Change<State>(currentState: this.state, nextState: state)); _state = state; _stateController.add(_state); _emitted = true; } @mustCallSuper void onChange(Change<State> change) { Bloc.observer.onChange(this, change); } @mustCallSuper void addError(Object error, [StackTrace? stackTrace]) { onError(error, stackTrace ?? StackTrace.current); } @protected @mustCallSuper void onError(Object error, StackTrace stackTrace) { Bloc.observer.onError(this, error, stackTrace); assert(() { throw BlocUnhandledErrorException(this, error, stackTrace); } ()); } @mustCallSuper Future<void> close() async { Bloc.observer.onClose(this); await _stateController.close(); }}

The BlocBase above does a few important things to comb through

Bloc. Observer this is not important, it is a class defined internally within the framework and can be ignored here, not very important

  1. Stores the incoming State object

    • Each time the emit is refreshed, the passed state is replaced by the stored state object
    • The emit determines that if the passed State is the same as the stored State object, the refresh operation will not be performed (that is why I added the clone method to the State class).
  2. Initializes a Stream set of objects
  3. Encapsulates the closure of a Stream
  • Simplify the above code
abstract class BlocBase<T> { BlocBase(this.state) : _stateController = StreamController<T>.broadcast(); final StreamController<T> _stateController; T state; bool _emitted = false; Stream<T> get stream => _stateController.stream; void emit(T newState) { if (_stateController.isClosed) return; if (state == newState && _emitted) return; state = newState; _stateController.add(state); _emitted = true; } @mustCallSuper Future<void> close() async { await _stateController.close(); }}

BlocBuilder

BlocBuilder simplifies the use of StreamBuilder a lot, so let’s take a look at the internals

  • BlocBuilder

    • Here you need to pay attention to the builder parameters; BuildWhen is a parameter to determine if updates are needed
    • The build method calls Builder, so you need to look at the parent class blocbuilderBase
typedef BlocWidgetBuilder<S> = Widget Function(BuildContext context, S state);

class BlocBuilder<B extends BlocBase<S>, S> extends BlocBuilderBase<B, S> {
  const BlocBuilder({
    Key? key,
    required this.builder,
    B? bloc,
    BlocBuilderCondition<S>? buildWhen,
  }) : super(key: key, bloc: bloc, buildWhen: buildWhen);

  final BlocWidgetBuilder<S> builder;

  @override
  Widget build(BuildContext context, S state) => builder(context, state);
}
  • BlocBuilderBase

    • Context.read < B>() provides the same effect as Provider.of

      (this, listen: false)
    • Here we get the XxxBloc object we passed in the blocProvider by context.read< B>() and assign it to the _bloc variable in the _blocbuilderBaseState
    • BlocbuilderBase abstracts a build method, assigning a blocListener to _blocbuilderBaseState
    • BlocbuilderBase is not yet able to see the refresh logic, several important parameters: _bloc, Listener, Widget. build are passed to blocListener; You need to look at the implementation of blocListener
abstract class BlocBuilderBase<B extends BlocBase<S>, S> extends StatefulWidget { const BlocBuilderBase({Key? key, this.bloc, this.buildWhen}) : super(key: key); final B? bloc; final BlocBuilderCondition<S>? buildWhen; Widget build(BuildContext context, S state); @override State<BlocBuilderBase<B, S>> createState() => _BlocBuilderBaseState<B, S>(); } class _BlocBuilderBaseState<B extends BlocBase<S>, S> extends State<BlocBuilderBase<B, S>> { late B _bloc; late S _state; @override void initState() { super.initState(); _bloc = widget.bloc ?? context.read<B>(); _state = _bloc.state; }... @override Widget build(BuildContext context) { ... return BlocListener<B, S>( bloc: _bloc, listenWhen: widget.buildWhen, listener: (context, state) => setState(() => _state = state), child: widget.build(context, _state), ); }}
  • BlocListener: The parameter is passed to the constructor of the parent class. You need to look at the implementation of the parent class bloclistenerBase
class BlocListener<B extends BlocBase<S>, S> extends BlocListenerBase<B, S>
  const BlocListener({
    Key? key,
    required BlocWidgetListener<S> listener,
    B? bloc,
    BlocListenerCondition<S>? listenWhen,
    Widget? child,
  }) : super(
          key: key,
          child: child,
          listener: listener,
          bloc: bloc,
          listenWhen: listenWhen,
        );
}
  • BloclistenerBase: Streamlined some logic code
abstract class BlocListenerBase<B extends BlocBase<S>, S> extends SingleChildStatefulWidget { const BlocListenerBase({ Key? key, required this.listener, this.bloc, this.child, this.listenWhen, }) : super(key: key, child: child); final Widget? child; final B? bloc; final BlocWidgetListener<S> listener; final BlocListenerCondition<S>? listenWhen; @override SingleChildState<BlocListenerBase<B, S>> createState() => _BlocListenerBaseState<B, S>(); } class _BlocListenerBaseState<B extends BlocBase<S>, S> extends SingleChildState<BlocListenerBase<B, S>> { StreamSubscription<S>? _subscription; late B _bloc; late S _previousState; @override void initState() { super.initState(); _bloc = widget.bloc ?? context.read<B>(); _previousState = _bloc.state; _subscribe(); }... @override Widget buildWithChild(BuildContext context, Widget? child) { return child! ; } @override void dispose() { _unsubscribe(); super.dispose(); } void _subscribe() { _subscription = _bloc.stream.listen((state) { if (widget.listenWhen? .call(_previousState, state) ?? true) { widget.listener(context, state); } _previousState = state; }); } void _unsubscribe() { _subscription? .cancel(); _subscription = null; }}

Finally found the key code!

You can see that Bloc is refreshed with the StreamController and Listen

The widget. Listener (context, state) is called. This method is implemented as setState, as you can see in the _blocbuilderBaseState class

_bloc.stream.listen( (state) { if (widget.listenWhen? .call(_previousState, state) ?? true) { widget.listener(context, state); } _previousState = state; });

Streamline BlocBuild

The above BlocBuild implementation logic is still too complicated and there are too many layers of encapsulation. Let’s write a condensed version of BlocBuild

Of course, the core logic that blocBuild refreshes will be retained

class BlocEasyBuilder<T extends BlocBase<V>, V> extends StatefulWidget { const BlocEasyBuilder({ Key? key, required this.builder, }) : super(key: key); final Function(BuildContext context, V state) builder; @override _BlocEasyBuilderState createState() => _BlocEasyBuilderState<T, V>(); } class _BlocEasyBuilderState<T extends BlocBase<V>, V> extends State<BlocEasyBuilder<T, V>> { late T _bloc; late V _state; StreamSubscription<V>? _listen; @override void initState() { _bloc = BlocProvider.of<T>(context); _state = _bloc.state; // Widget _listen = _Bloc. Stream. Listen ((event) {setState(() {}); }); super.initState(); } @override Widget build(BuildContext context) { return widget.builder(context, _state); } @override void dispose() { _listen? .cancel(); super.dispose(); }}
  • Take a look at the renderings: for detailed usage code, see: FLUTTER_USE

The Event mechanism

If developed using Bloc mode, there is an additional Event layer that defines all Event interactions

Let me just mention over here

  • Bloc: Some code has been omitted
abstract class Bloc<Event, State> extends BlocBase<State> { /// {@macro bloc} Bloc(State initialState) : super(initialState) { _bindEventsToStates(); } StreamSubscription<Transition<Event, State>>? _transitionSubscription; StreamController<Event>? __eventController; StreamController<Event> get _eventController { return __eventController ?? = StreamController<Event>.broadcast(); } void add(Event event) { if (_eventController.isClosed) return; try { onEvent(event); _eventController.add(event); } catch (error, stackTrace) { onError(error, stackTrace); } } Stream<Transition<Event, State>> transformEvents( Stream<Event> events, TransitionFunction<Event, State> transitionFn, ) { return events.asyncExpand(transitionFn); } @protected @visibleForTesting @override void emit(State state) => super.emit(state); Stream<State> mapEventToState(Event event); Stream<Transition<Event, State>> transformTransitions( Stream<Transition<Event, State>> transitions, ) { return transitions; } @override @mustCallSuper Future<void> close() async { await _eventController.close(); await _transitionSubscription? .cancel(); return super.close(); } void _bindEventsToStates() { _transitionSubscription = transformTransitions( transformEvents( _eventController.stream,  (event) => mapEventToState(event).map( (nextState) => Transition( currentState: state, event: event, nextState: nextState, ), ), ), ).listen( (transition) { if (transition.nextState == state && _emitted) return; try { emit(transition.nextState); } catch (error, stackTrace) { onError(error, stackTrace); } }, onError: onError, ); }}

The overall logic is clear. Let’s get it sorted out

  1. Bloc is an abstract class

    • The _bindEventStoStates () method is called inside the constructor
    • Bloc abstracts a MapEventToState (Event Event) method that inherits the Bloc abstract class and must be implemented
  2. In the Bloc class, an instance of a Stream object is used to trigger events

    • When an Event Event is added, the Listener callback in the _bindEventStoStates () method is triggered
  3. _bindEventStoStates does something

    • Add Event: events.asyncExpand(transitionFn); First pass your Event parameter into the transitionFn method for execution
    • TransitionFn can be used to pass the Event parameter into the MapEventToState object, and the MapEventToState object can be passed back
    • Then trigger the LISTEN callback, in which the state is passed to the emit, and then trigger the refresh control to rebuild

conclusion

After analyzing the key classes above, the entire Bloc mechanism becomes clear

BlocProvider

  • Responsible for storing incoming XXXBloc for storage
  • The of method that gets the stored XXXBloc at the locProvider or its child nodes location is provided
  • Provide a callback to recycle the resource (recycle the Stream)

BlocBase

  • Stores the incoming State object
  • Initializes a Stream set of objects
  • Encapsulates the closure of a Stream

BlocBuilder

  • Nature is StatefulWidget
  • The blocProvider gets the XXXBloc and listens for data changes through its listener method
  • When the data is changed, the StatefulWidget will be restored via setState to provide a partial refresh

    # Hand rub a state management framework

The principles of Bloc are much simpler than those of Provider…

Mimicking Bloc’s refresh mechanism, hand rub a state management framework! Let’s name it EasyC!

Hand made

  • EASYC: First you need to write a base class that handles a series of Stream operations
abstract class EasyC<T> { EasyC(this.state) : _controller = StreamController<T>.broadcast(); final StreamController<T> _controller; T state; bool _emitted = false; Stream<T> get stream => _controller.stream; void emit(T newState) { if (_controller.isClosed) return; if (state == newState && _emitted) return; state = newState; _controller.add(state); _emitted = true; } @mustCallSuper Future<void> close() async { await _controller.close(); }}
  • EasyCProvider

    • We don’t use the InheritedProvider provided by the Provider framework
    • Here I’ve used an inheritedWidget and rubbed it in my hand
    • The “of” method and the stream closure are done; No need to close the stream manually and no need to write StatefulWidget!
class EasyCProvider<T extends EasyC> extends InheritedWidget {
  EasyCProvider({
    Key? key,
    Widget? child,
    required this.create,
  }) : super(key: key, child: child ?? Container());

  final T Function(BuildContext context) create;

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) => false;

  @override
  InheritedElement createElement() => EasyCInheritedElement(this);

  static T of<T extends EasyC>(BuildContext context) {
    var inheritedElement =
        context.getElementForInheritedWidgetOfExactType<EasyCProvider<T>>()
            as EasyCInheritedElement<T>?;

    if (inheritedElement == null) {
      throw 'not found';
    }

    return inheritedElement.value;
  }
}

class EasyCInheritedElement<T extends EasyC> extends InheritedElement {
  EasyCInheritedElement(EasyCProvider<T> widget) : super(widget);

  bool _firstBuild = true;

  late T _value;

  T get value => _value;

  @override
  void performRebuild() {
    if (_firstBuild) {
      _firstBuild = false;
      _value = (widget as EasyCProvider<T>).create(this);
    }

    super.performRebuild();
  }

  @override
  void unmount() {
    _value.close();
    super.unmount();
  }
}
  • EasyCBuilder: The last whole fixed point refresh Widget
class EasyCBuilder<T extends EasyC<V>, V> extends StatefulWidget { const EasyCBuilder({ Key? key, required this.builder, }) : super(key: key); final Function(BuildContext context, V state) builder; @override _EasyCBuilderState createState() => _EasyCBuilderState<T, V>(); } class _EasyCBuilderState<T extends EasyC<V>, V> extends State<EasyCBuilder<T, V>> { late T _easyC; late V _state; StreamSubscription<V>? _listen; @override void initState() { _easyC = EasyCProvider.of<T>(context); _state = _easyC.state; // Widget _listen = _easyc.stream. listen((event) {setState(() {}); }); super.initState(); } @override Widget build(BuildContext context) { return widget.builder(context, _state); } @override void dispose() { _listen? .cancel(); super.dispose(); }}

These three files basically reproduce the Bloc refresh mechanism

It also removes a bug in my mind that the Bloc source code uses the Provider’s _startListening method inexplicably…

use

The use is basically the same as Bloc

I was going to remove the decision to compare the emits of the two new and old state objects, but the Bloc author seems to have a strong attachment to this concept and has dealt with it in many places; So, here I keep it, and I can keep the original Bloc usage

  • view
class CounterEasyCPage extends StatelessWidget { final easyC = CounterEasyC(); @override Widget build(BuildContext context) { return EasyCProvider( create: (BuildContext context) => easyC, child: Scaffold(appBar: appBar (title: Text(' custom state management framework -EasyC example ')), body: Center(child: EasyCBuilder<CounterEasyC, CounterEasyCState>( builder: (context, state) {return Text(' ${easyc.state.count} ', style: textStyle (fontSize: 30.0),); }, ), ), floatingActionButton: FloatingActionButton( onPressed: () => easyC.increment(), child: Icon(Icons.add), ), ), ); }}
  • logic
class CounterEasyC extends EasyC<CounterEasyCState> { CounterEasyC() : super(CounterEasyCState().init()); // increment void increment() => emit(State.clone ().. count = ++state.count); }
  • state
class CounterEasyCState { late int count; CounterEasyCState init() { return CounterEasyCState().. count = 0; } CounterEasyCState clone() { return CounterEasyCState().. count = count; }}
  • rendering

The Provider is not different from the global Provider, so I will not repeat it here

conclusion

This hand-rubbed EASYC frame retains the essence of the Bloc refresh mechanism, while at the same time doing a lot of streamlining

I believe that predestined people as long as the heart to see, will be able to understand

The Bloc source code is not complicated, it is a very streamlined use of Stream, basically using pain points, all encapsulated and handled internally

The last

Message board

The Provider and Bloc source parsing is finished, but we need the last GetX post…

In order to prove that the analysis source code I wrote is useful and effective, at the end, I hand rubbed a new state management framework according to the refresh mechanism of its state management framework

Choosing a state management framework should be a matter of caution; You can take a look at its principle in advance, understand his internal mechanism of operation, you can go to the need to choose, because you understand its internal mechanism of operation, even if the use of the process of what the problem, you can calmly deal with; If you’re worried about the author dropping it or not being happy with its functionality, pick the refresh mechanism you want and rub one in your hands!

Provider, Bloc, and Getx are the three frameworks I have written for, and if you choose any one of them, they will help you do some rework

The relevant address

  • The Github address of the Demo in this article is flutter_use
  • Web: https://cnad666.github.io/flu…

    • If the function button is not visible, you may need to clear the browser cache
  • Windows: Windows platform installation package

    • Password: xdd666

series

  • The Flutter Provider is the other side of the Flutter Provider.
  • Handler Those Things (10,000 words)
  • ThreadLocal: ThreadLocal: ThreadLocal: ThreadLocal