博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Flutter学习篇(三)—— MobX的使用和原理
阅读量:6511 次
发布时间:2019-06-24

本文共 9663 字,大约阅读时间需要 32 分钟。

导航

前言

是前端一个很流行的函数响应式编程,让状态变得简单可扩展。背后的哲学是:

任何源自应用状态的东西都应该自动地获得

基于观察者的MVVM框架完成了数据到UI的双向绑定。Google2017年也发布了类似思想的MVVM框架ViewModel。MVVM是数据驱动更新的框架,可以很方便地把页面和逻辑抽开,在前端很受欢迎。所以MobX也出了dart的版本用来支持Flutter的使用。下面我们就开始动手在Flutter引入MobX。

使用

先放出,使用分几步走:

1. 首先,引入依赖:

mobx: ^0.2.0  flutter_mobx: ^0.2.0  mobx_codegen: ^0.2.0复制代码

2. 添加一个store:

import 'package:mobx/mobx.dart';import 'package:shared_preferences/shared_preferences.dart';// 自动生成的类part 'settings_store.g.dart';class SettingsStore = _SettingsStore with _$SettingsStore;abstract class _SettingsStore implements Store {  var key = {    "showPage":"showPage",  };  @observable  String showPage = "";  @action  getPrefsData() async {    SharedPreferences prefs = await SharedPreferences.getInstance();    showPage = prefs.get(key["showPage"]) ?? "首页";  }  @action  saveShowPage(String showPage) async {    if(showPage == null) {      return;    }    this.showPage = showPage;    SharedPreferences prefs = await SharedPreferences.getInstance();    prefs.setString(key["showPage"], showPage);  }}复制代码

对于dart版本的mobx,是通过生成新的类来实现双向绑定的效果,所以需要在store里面加上生成类的一些定义:

part 'settings_store.g.dart';class SettingsStore = _SettingsStore with _$SettingsStore;复制代码

_$SettingsStore是待生成的类,SettingsStore则是混合了两个store的新类。如下是自动生成的类:

// GENERATED CODE - DO NOT MODIFY BY HANDpart of 'settings_store.dart';// **************************************************************************// StoreGenerator// **************************************************************************// ignore_for_file: non_constant_identifier_names, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_charsmixin _$SettingsStore on _SettingsStore, Store {  final _$showPageAtom = Atom(name: '_SettingsStore.showPage');  @override  String get showPage {    _$showPageAtom.reportObserved();    return super.showPage;  }  @override  set showPage(String value) {    _$showPageAtom.context.checkIfStateModificationsAreAllowed(_$showPageAtom);    super.showPage = value;    _$showPageAtom.reportChanged();  }  final _$getPrefsDataAsyncAction = AsyncAction('getPrefsData');  @override  Future getPrefsData() {    return _$getPrefsDataAsyncAction.run(() => super.getPrefsData());  }  final _$saveShowPageAsyncAction = AsyncAction('saveShowPage');  @override  Future saveShowPage(String showPage) {    return _$saveShowPageAsyncAction.run(() => super.saveShowPage(showPage));  }}复制代码

要实现上面的效果还需要分几步走:

  • 在需要被观察的数据增加@observable注解,需要执行操作的方法增加@action注解,

  • 接着执行flutter packages pub run build_runner build

  • 就会自动生成上述的类,特别的是,如果需要实时跟踪store的变化从而实时改变新生成的类,需要执行一个命令:

    flutter packages pub run build_runner watch , 如果操作失败了,可以尝试下面的clean命令:

    flutter packages pub run build_runner watch --delete-conflicting-outputs

3. 在widget中使用:

在需要观察数据变化的widget套上一层Observer widget,

_buildShowPageLine(BuildContext context) {    return GestureDetector(        onTap: () {          showDialog
( context: context, builder: (context) { String selectValue = '${settingsStore.showPage}'; List
valueList = ["首页", "生活"]; return RadioAlertDialog(title: "选择展示页面", selectValue: selectValue, valueList: valueList); }).then((value) { print(value); settingsStore.saveShowPage(value); }); }, // 在需要观察变化的widget套上一层Observer widget, child: Observer( builder: (_) => ListTile( title: Common.primaryTitle(content: "默认展示页面"), subtitle: Common.primarySubTitle(content: '${settingsStore.showPage}'), ) )); }复制代码

完成上述步骤就可以通过对store的数据进行操作,从而自动刷新widget。

原理

看完上述的使用之后,相信读者会感到又疑惑又神奇。别急,接下来就进入原理的剖析。

首先看到新生成的代码_$SettingsStore,其中有几处关键的插桩代码,

@override  String get showPage {    _$showPageAtom.reportObserved();    return super.showPage;  }  @override  set showPage(String value) {    _$showPageAtom.context.checkIfStateModificationsAreAllowed(_$showPageAtom);    super.showPage = value;    _$showPageAtom.reportChanged();  }复制代码

可以看到在获取变量时,会调用dart reportObserved(), 设置变量会调用dart reportChanged, 从名字就可以看出获取变量就是将变量上报,变为被观察的状态,设置变量其实就是上报数据变化,进行通知。

我们先看看reportObserved()做了什么,

// atom可以理解为对应的被观察变量的封装  void _reportObserved(Atom atom) {    final derivation = _state.trackingDerivation;    if (derivation != null) {      derivation._newObservables.add(atom);      if (!atom._isBeingObserved) {        atom          .._isBeingObserved = true          .._notifyOnBecomeObserved();      }    }  }复制代码

可以看出核心就是把当前的变量加入被观察的队列中去。

reportChanged做的是啥呢,

void propagateChanged(Atom atom) {    if (atom._lowestObserverState == DerivationState.stale) {      return;    }    atom._lowestObserverState = DerivationState.stale;    for (final observer in atom._observers) {      if (observer._dependenciesState == DerivationState.upToDate) {        observer._onBecomeStale();      }      observer._dependenciesState = DerivationState.stale;    }  }复制代码

关键的代码是

if (observer._dependenciesState == DerivationState.upToDate) {    observer._onBecomeStale(); }复制代码

当数据需要更新的时候,调用观察者的_onBecomeStale方法,看到这里,相信聪明的读者应该会记起观察者的存在了。 那就是我们用了被观察数据的widget上面套着的Observer的widget。源码如下:

library flutter_mobx;// ignore_for_file:implementation_importsimport 'package:flutter/widgets.dart';import 'package:mobx/mobx.dart';import 'package:mobx/src/core.dart' show ReactionImpl;/// Observer observes the observables used in the `builder` function and rebuilds the Widget/// whenever any of them change. There is no need to do any other wiring besides simply referencing/// the required observables.////// Internally, [Observer] uses a `Reaction` around the `builder` function. If your `builder` function does not contain/// any observables, [Observer] will throw an [AssertionError]. This is a debug-time hint to let you know that you are not observing any observables.class Observer extends StatefulWidget {  /// Returns a widget that rebuilds every time an observable referenced in the  /// [builder] function is altered.  ///  /// The [builder] argument must not be null. Use the [context] to specify a ReactiveContext other than the `mainContext`.  /// Normally there is no need to change this. [name] can be used to give a debug-friendly identifier.  const Observer({
@required this.builder, Key key, this.context, this.name}) : assert(builder != null), super(key: key); final String name; final ReactiveContext context; final WidgetBuilder builder; @visibleForTesting Reaction createReaction(Function() onInvalidate) { final ctx = context ?? mainContext; return ReactionImpl(ctx, onInvalidate, name: name ?? 'Observer@${ctx.nextId}'); } @override State
createState() => _ObserverState(); void log(String msg) { debugPrint(msg); }}class _ObserverState extends State
{ ReactionImpl _reaction; @override void initState() { super.initState(); _reaction = widget.createReaction(_invalidate); } void _invalidate() => setState(noOp); static void noOp() {} @override Widget build(BuildContext context) { Widget built; dynamic error; _reaction.track(() { try { built = widget.builder(context); } on Object catch (ex) { error = ex; } }); if (!_reaction.hasObservables) { widget.log( 'There are no observables detected in the builder function for ${_reaction.name}'); } if (error != null) { throw error; } return built; } @override void dispose() { _reaction.dispose(); super.dispose(); }}复制代码

猜猜我们看到了什么, Observer继承自StatefulWidget,看到这里应该就豁然开朗了吧,其实就是在我们的widget上面套了一个父的widget,并且是StatefulWidget类型的,这样一来,只要更新了父widget,同样的我们的widget也就可以进行更新了。

在build的过程,可以看到调用了track方法,跟踪源码可以发现就是先调用了传入的方法(这里对应的是我们widget的构建),然后就是把Observer插入观察者队列:

void _bindDependencies(Derivation derivation) {    final staleObservables =        derivation._observables.difference(derivation._newObservables);    final newObservables =        derivation._newObservables.difference(derivation._observables);    var lowestNewDerivationState = DerivationState.upToDate;    // Add newly found observables    for (final observable in newObservables) {      observable._addObserver(derivation);      // Computed = Observable + Derivation      if (observable is Computed) {        if (observable._dependenciesState.index >            lowestNewDerivationState.index) {          lowestNewDerivationState = observable._dependenciesState;        }      }    }    // Remove previous observables    for (final ob in staleObservables) {      ob._removeObserver(derivation);    }    if (lowestNewDerivationState != DerivationState.upToDate) {      derivation        .._dependenciesState = lowestNewDerivationState        .._onBecomeStale();    }    derivation      .._observables = derivation._newObservables      .._newObservables = {}; // No need for newObservables beyond this point  }复制代码

接着我们需要找出观察者的_onBecomeStale方法,如果跟踪_onBecomeStale方法,可以发现最终调用的是reaction的run方法:

@override  void _run() {    if (_isDisposed) {      return;    }    _context.startBatch();    _isScheduled = false;    if (_context._shouldCompute(this)) {      try {        _onInvalidate();      } on Object catch (e) {        // Note: "on Object" accounts for both Error and Exception        _errorValue = MobXCaughtException(e);        _reportException(e);      }    }    _context.endBatch();  }复制代码

其中的_onInvalidate()就是在observer构成的时候传入的方法:

void _invalidate() => setState(noOp);  static void noOp() {}复制代码

看到这里,其实已经水落石出了,就是通过调用的setState从而刷新了widget。

总结

对于Mobx,本质就是在使用了被观察数据的widget上面套了一个父的widget,而这个父的widget是一个StatefulWidget。 然后通过观察者模式,发现数据更改时,通知观察者,然后观察者调用了setState了,更新了Observer,从而最后达到刷新子widget的效果。

仓库

点击,查看完整代码。

转载地址:http://uwcfo.baihongyu.com/

你可能感兴趣的文章