Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

The introduction

For the management of Flutter state, the company’s project uses the Bloc scheme. Bloc is essentially an encapsulated extension library of the provider, which implements state change notifications via inheritedWidgets, Notifier, and Stream streams.

About the Bloc realization principle, interested students can watch this article Bloc principle analysis

RxBinder

Quite apart from theBlocInternal implementation strategy, Xiaohong tries to customize a set of state management tools based on data-driven model. The idea is as follows:The main members are as follows:

  • RxBinderData: data model base class that internally encapsulates change notifications
  • RxBinder: core class for associating subscriptions

Code implementation

Create a utility class for registering and sending notifications

///Manage notifications using the form callback
class RxNotifier {
  List<VoidCallback> _listeners = [];

  void addListener(VoidCallback listener) {
    _listeners.add(listener);
  }

  void remove(VoidCallback listener) {
    if(_listeners.contains(listener)) { _listeners.remove(listener); }}///notice
  void notify() {
    if (_listeners.isEmpty) return;
    for (final entry in_listeners) { entry.call(); }}}Copy the code

The data model should have two features: adding listeners when the data is being used; Send change notifications when data changes.

///Data model base classes, (encapsulating change notifications)
class RxBinderData<T> {
  late T _value;
  late String uuid;
  RxNotifier subject = RxNotifier();
  RxBinder? _rxBinder;

  RxBinderData(this._value, {RxBinder? value}) {
    uuid = Uuid().v4();
    bindRx(value);
  }

  void bindRx(RxBinder? value) {
    _rxBinder = value;
  }

  @override
  String toString() {
    return value.toString();
  }

  T get value {
    // Add listener, change notification register_rxBinder? .register(uuid, subject);return _value;
  }

  set value(T val) {
    _value = val;
    notify();
  }

  void notify() {
    // Notify that data has changedsubject.notify(); }}Copy the code

Create a transit utility class for unified management of message distribution and subscription when data changes

class RxBinder {
  Map<RxNotifier, String> _subjects = {};

  ///Subscriber. Key is the data ID of the subscription and value is the notification callback when the subscription data changes
  Map<String.List<VoidCallback>> _subscriber = {};

  / / register
  void register(String uuid, RxNotifier subject) {
    if(! _subjects.containsKey(subject)) { subject.addListener(() { _notify(uuid); }); _subjects[subject] =' '; }}// Add a subscription
  void addListener(String uuid, VoidCallback listener) {
    if(! _subscriber.containsKey(uuid)) {/ / key does not exist
      _subscriber[uuid] = [listener];
    } else {
      / / the key already exists
      List<VoidCallback> list = _subscriber[uuid]! ;if(! list.contains(listener)) { list.add(listener); _subscriber[uuid] = list; }}}// Notify subscribers
  void _notify(String uuid) {
    if (_subscriber.containsKey(uuid)) {
      final list = _subscriber[uuid];
      if(list ! =null && list.isNotEmpty) {
        for (final entry in list) {
          entry.call();
        }
      }
    }
  }
}
Copy the code

Control refresh component

typedef WidgetCallback = Widget Function(BuildContext context);

class RxBindWidget extends StatefulWidget {
  final WidgetCallback builder;
  final List<RxBinderData> binders;

  const RxBindWidget(this.builder, this.binders, {Key? key}) : super(key: key);

  @override
  _RxBindWidgetState createState() => _RxBindWidgetState();
}

class _RxBindWidgetState extends State<RxBindWidget> {
  RxBinder rxBinder = RxBinder();

  @override
  void initState() {
    super.initState();
    for (final entity in widget.binders) {
      // Data source binding Rxentity.bindRx(rxBinder); rxBinder.addListener(entity.uuid, notifyDataChange); }}void notifyDataChange() {
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    returnwidget.builder(context); }}Copy the code

Demo works perfectly

///The underlying data type is chestnut int
extension IntExtension on int {
  RxInt get rex => RxInt(this);

  // Bind Rx notifications
  voidbindRx(RxBinder? value) { rex.bindRx(value); }}///Extension classes for concrete business
class RxInt extends RxBinderData<int> {
  RxInt(int value) : super(value);

  RxInt operator+ (int other) {
    value = value + other;
    return this;
  }

  RxInt operator- (int other) {
    value = value - other;
    return this; }}Copy the code
class Logic {
  RxInt count = 0.rex;

  void increase() => ++count;
}

class TestRxBinder extends StatelessWidget {
  final Logic logic = Logic();

  TestRxBinder({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RxBindWidget((context) {
          return _child(context);
        }, [logic.count]),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => logic.increase(),
        child: Icon(Icons.add),
      ),
    );
  }

  Widget _child(BuildContext context) {
    return Text(
      'click on the${logic.count.value}Time ',); }}Copy the code

The scheme in this paper is not perfect, welcome to discuss in the comment section ~