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 theBloc
Internal 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 notificationsRxBinder
: 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 ~