Flutter圈子FlutterFlutter

Flutter里的状态机制

2020-04-04  本文已影响0人  LSteven

最近开始写flutter了, 觉得flutter里的状态同步机制还是挺有意思的,总结一下。

这里是比较好的讲解文章,基于它做一些源码分析了解下原理。

前提: 已经知道应该咋用,也知道InheritedWidget是啥了。

Listenable

abstract class AnimatedWidget extends StatefulWidget {

  const AnimatedWidget({
    Key key,
    @required this.listenable,
  }) : assert(listenable != null),
       super(key: key);


  final Listenable listenable;

  @protected
  Widget build(BuildContext context);

  /// Subclasses typically do not override this method.
  @override
  _AnimatedState createState() => _AnimatedState();

}

class _AnimatedState extends State<AnimatedWidget> {
  @override
  void initState() {
    super.initState();
    widget.listenable.addListener(_handleChange);
  }

  @override
  void didUpdateWidget(AnimatedWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.listenable != oldWidget.listenable) {
      oldWidget.listenable.removeListener(_handleChange);
      widget.listenable.addListener(_handleChange);
    }
  }

  @override
  void dispose() {
    widget.listenable.removeListener(_handleChange);
    super.dispose();
  }

  void _handleChange() {
    setState(() {
      // The listenable's state is our build state, and it changed already.
    });
  }

  @override
  Widget build(BuildContext context) => widget.build(context);
}

我们看到使用listenable的组件,会将自己注册到listener中去,当收到监听时调用_handleChange() => setState方法调用自己的build方法。不需要使用方自己再调用setState,也可以看到这里的作用域。

ScopedModel

[图片上传失败...(image-876db0-1585971080030)]

class _InheritedModel<T extends Model> extends InheritedWidget {
  final T model;
  final int version;

  _InheritedModel({Key key, Widget child, T model})
      : this.model = model,
        this.version = model._version,
        super(key: key, child: child);

  @override
  bool updateShouldNotify(_InheritedModel<T> oldWidget) =>
      (oldWidget.version != version);
}

包含数据模型,通过继承InheritedWidget实现在父widget不需要一层层将model传给子widget

abstract class Model extends Listenable {
  final Set<VoidCallback> _listeners = Set<VoidCallback>();
  int _version = 0;
  int _microtaskVersion = 0;

  /// [listener] will be invoked when the model changes.
  @override
  void addListener(VoidCallback listener) {
    _listeners.add(listener);
  }

  /// [listener] will no longer be invoked when the model changes.
  @override
  void removeListener(VoidCallback listener) {
    _listeners.remove(listener);
  }

  /// Returns the number of listeners listening to this model.
  int get listenerCount => _listeners.length;

  /// Should be called only by [Model] when the model has changed.
  @protected
  void notifyListeners() {
    // We schedule a microtask to debounce multiple changes that can occur
    // all at once.
    if (_microtaskVersion == _version) {
      _microtaskVersion++;
      scheduleMicrotask(() {
        _version++;
        _microtaskVersion = _version;

        // Convert the Set to a List before executing each listener. This
        // prevents errors that can arise if a listener removes itself during
        // invocation!
        _listeners.toList().forEach((VoidCallback listener) => listener());
      });
    }
  }

Model继承前面说到的Listenable,当主动调用notifyListeners时,listeners会收到通知
那么listeners是什么呢?

widget外包了一层ScopedModel,后面看到很多状态管理机制都是这种做法

class ScopedModel<T extends Model> extends StatelessWidget {
  /// The [Model] to provide to [child] and its descendants.
  final T model;

  /// The [Widget] the [model] will be available to.
  final Widget child;

  ScopedModel({@required this.model, @required this.child})
      : assert(model != null),
        assert(child != null);

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: model,
      builder: (context, _) => _InheritedModel<T>(model: model, child: child),
    );
  }

  
  static T of<T extends Model>(
    BuildContext context, {
    bool rebuildOnChange = false,
  }) {
    final Type type = _type<_InheritedModel<T>>();

    Widget widget = rebuildOnChange
        ? context.inheritFromWidgetOfExactType(type)
        : context.ancestorWidgetOfExactType(type);

    if (widget == null) {
      throw new ScopedModelError();
    } else {
      return (widget as _InheritedModel<T>).model;
    }
  }

  static Type _type<T>() => T;
}
return AnimatedBuilder(animation: model,
  builder: (context, _) => _InheritedModel<T>(model: model, child: child),
);

关键代码:
这里和InheritedWidget不一样,就不需要setState了,notify时AnimatedBuilderbuild函数会重新执行

class ScopedModelDescendant<T extends Model> extends StatelessWidget {
  /// Called whenever the [Model] changes.
  final ScopedModelDescendantBuilder<T> builder;

  /// An optional constant child that does not depend on the model.  This will
  /// be passed as the child of [builder].
  final Widget child;

  /// An optional constant that determines whether the
  final bool rebuildOnChange;

  /// Constructor.
  ScopedModelDescendant({
    @required this.builder,
    this.child,
    this.rebuildOnChange = true,
  });

  @override
  Widget build(BuildContext context) {
    return builder(
      context,
      child,
      ScopedModel.of<T>(context, rebuildOnChange: rebuildOnChange),
    );
  }
}

这里提供了ScopedModelDescendantmodel通过ScopedModel.of获取好给你,如果不想用这个自己通过ScopedModel.of也没啥问题。

更新范围

ScopeModel下的child如果没有使用ScopedModel.ofbuild吗?, 会, 因为完全setState了,只是不会调用didChangeDependencies而已。可以看这里的解释,毕竟scopeModel其实就是InheritedWidget&setState的组合,多了一个model层而已,而且model还必须继承它的model,侵入性较强

网上也有ScopeModel的推荐用法
[图片上传失败...(image-80b027-1585971080030)]
[图片上传失败...(image-b9a12f-1585971080030)]

redux

和前端的redux差不多,

为什么要使用redux,讲的很好,尤其是那几张动画,灰常好。

[图片上传失败...(image-56911a-1585971080030)]

class Store<State> {
  /// The [Reducer] for your Store. Allows you to get the current reducer or
  /// replace it with a new one if need be.
  Reducer<State> reducer;

  final StreamController<State> _changeController;
  State _state;
  List<NextDispatcher> _dispatchers;
  ReducerCallback<State> _reducerCallback;

  Store(
    this.reducer, {
    State initialState,
    List<Middleware<State>> middleware = const [],
    bool syncStream: false,
    bool distinct: false,
  })
      : _changeController = new StreamController.broadcast(sync: syncStream) {
    _state = initialState;
    _dispatchers = _createDispatchers(
      middleware,
      _createReduceAndNotify(distinct),
    );
  }

  /// Returns the current state of the app
  State get state => _state;

  
  NextDispatcher _createReduceAndNotify(bool distinct) {
    return (dynamic action) {
      final state = reducer(_state, action);

      if (distinct && state == _state) return;

      _state = state;
      _changeController.add(state);

      if (_reducerCallback != null) {
        _reducerCallback(state, action);
      }
    };
  }

  void _setReducerCallback(ReducerCallback callback) {
    _reducerCallback = callback;
  }

  List<NextDispatcher> _createDispatchers(
    List<Middleware<State>> middleware,
    NextDispatcher reduceAndNotify,
  ) {
    final dispatchers = <NextDispatcher>[]..add(reduceAndNotify);

    // Convert each [Middleware] into a [NextDispatcher]
    for (var nextMiddleware in middleware.reversed) {
      final next = dispatchers.last;

      dispatchers.add(
        (dynamic action) => nextMiddleware(this, action, next),
      );
    }

    return dispatchers.reversed.toList();
  }


  void dispatch(dynamic action) {
    _dispatchers[0](action);
  }


  Future teardown() async {
    _state = null;
    return _changeController.close();
  }
}

通过converter过滤stream,以免不必要的刷新,(改进了ScopeModel,不用setState导致全部子节点刷新

Stream<S> _stream = widget.store.onChange;

if (widget.ignoreChange != null) {
  _stream = _stream.where((state) => !widget.ignoreChange(state));
}

stream = _stream.map((_) => widget.converter(widget.store));

provider

[图片上传失败...(image-33e89f-1585971080030)]

provider会将widget代理给delegate

 @override
  Widget build(BuildContext context) {
    final delegate = this.delegate as _ListenableDelegateMixin<T>;
    return InheritedProvider<T>(
      value: delegate.value,
      updateShouldNotify: delegate.updateShouldNotify,
      child: child,
    );
  }
mixin _ListenableDelegateMixin<T extends Listenable> on ValueStateDelegate<T> {
  UpdateShouldNotify<T> updateShouldNotify;
  VoidCallback _removeListener;

  @override
  void initDelegate() {
    super.initDelegate();
    if (value != null) startListening(value);
  }

  @override
  void didUpdateDelegate(StateDelegate old) {
    super.didUpdateDelegate(old);
    final delegate = old as _ListenableDelegateMixin<T>;

    _removeListener = delegate._removeListener;
    updateShouldNotify = delegate.updateShouldNotify;
  }

  void startListening(T listenable, {bool rebuild = false}) {

    var buildCount = 0;
    final setState = this.setState;
    final listener = () => setState(() => buildCount++);

    var capturedBuildCount = buildCount;

    if (rebuild) capturedBuildCount--;
    updateShouldNotify = (_, __) {
      final res = buildCount != capturedBuildCount;
      capturedBuildCount = buildCount;
      return res;
    };

    listenable.addListener(listener);
    _removeListener = () {
      listenable.removeListener(listener);
      _removeListener = null;
      updateShouldNotify = null;
    };
  }

  @override
  void dispose() {
    _removeListener?.call();
    super.dispose();
  }
}

所以其实很好理解,在child外包了一层ListenerProvider,传入一个listener 组件初始化时开始监听这个listener,当notiify时监听函数收到回调,setState即可进行刷新。

但最底层其实也是InheritedWidget的作用

selector

希望provider实现redux-converter的能力,即只监听model的部分属性:

 @override
  Widget build(BuildContext context) {
    final selected = widget.selector(context);

    if (oldWidget != widget || selected != value) {
      value = selected;
      oldWidget = widget;
      cache = widget.builder(
        context,
        selected,
        widget.child,
      );
    }
    return cache;
  }

很简单,刷新的时候用Function selector判断下值是否有变化, 使用cache反抗inheritedWidgetd的重复build

异步化

streamController结合, 只是将listenable改成了stream

@override
Widget build(BuildContext context) {
return StreamBuilder<T>(
  stream: delegate.value,
  initialData: initialData,
  builder: (_, snapshot) {
    return InheritedProvider<T>(
      value: _snapshotToValue(snapshot, context, catchError, this),
      child: child,
      updateShouldNotify: updateShouldNotify,
    );
  },
);
}

bloc-业务逻辑组件 (Business Logic Component)

讲的很好

总的来说,通过异步事件流 + StreamBuilder分离UI与逻辑,同时减少build范围。

Widget即UI只需要关心自己需要的事件,同时发出自己的行为。

强制重建的setState()几乎从未使用过

[图片上传失败...(image-62ef02-1585971080030)]

其实和redux挺像的,少了action绕一圈罢了。

下篇结合mvvm对比下各个机制

上一篇下一篇

猜你喜欢

热点阅读