美文网首页flutter
状态State深入理解

状态State深入理解

作者: 程序员要多喝水 | 来源:发表于2020-04-22 23:19 被阅读0次

State

1 what is state?

State is information that (1) can be read synchronously when the widget is built and (2) might change during the lifetime of the widget. It is the responsibility of the widget implementer to ensure that the [State] is promptly notified when such state changes, using [State.setState].

中文含义大致是:

  • (1)当Widget构建时候可以同步读取状态信息;
  • (2)[State.setState]可触发Widget的State状态更新,Widget生命周期被回调时候可能State信息会发生改变;

2. StatefulWidget和State关系

熟知Flutter的童鞋应该都知道StatelessWidget,StatefulWidget,InheritedWidget,其中StatefulWidget,InheritedWidget都和State状态有关,而StatelessWidget是和状态无关;
先来分析其中StatefulWidget和State的具体关系:

abstract class StatefulWidget extends Widget {
     const StatefulWidget({ Key key }) : super(key: key);
     
      StatefulElement createElement() => StatefulElement(this);
      
      State createState();
}

正常自己创建一个StatefulWidget代码如下:

class BodyHome extends StatefulWidget {
  @override
  _BodyHomeState createState() => _BodyHomeState();
}

class _BodyHomeState extends State<BodyHome> {
  int count = 0;
  @override
  Widget build(BuildContext context) {
    print('_BodyHomeState rebuild');
    return Center(
      child: RaisedButton(
        onPressed: () {
          setState(() {
            count++;
          });
        },
        child: Text('$count'),
      ),
    );
  }
}
}

2.1 createElement/createState是什么,何时调用?

Flutter程序入口在main函数,main-->runApp-->scheduleAttachRootWidget-->attachRootWidget-->attachToRenderTree会发现createElement的地方,如下:

 void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}

 @protected
  void scheduleAttachRootWidget(Widget rootWidget) {
    Timer.run(() {
      attachRootWidget(rootWidget);
    });
  }
  
 void attachRootWidget(Widget rootWidget) {
    _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
      container: renderView,
      debugShortDescription: '[root]',
      child: rootWidget,
    ).attachToRenderTree(buildOwner, renderViewElement);
  }
  
   RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
    if (element == null) {
      owner.lockState(() {
        element = createElement();
        ...
   }        

因此程序一运行,加载时候会创建Element,再来看createState啥时候被调用呢?

StatefulElement createElement() => StatefulElement(this);

class StatefulElement extends ComponentElement {

  State<StatefulWidget> get state => _state;
  State<StatefulWidget> _state;
  
  /// Creates an element that uses the given widget as its configuration.
  StatefulElement(StatefulWidget widget)
      : _state = widget.createState(),
        super(widget) {
        ...
}

其实在调用createElement创建StatefulElement时候,就会发现widget.createState,此时createState会被调用,将state状态保存在_state,外部通过get state方法获取。并且StatefulElement的build方法其实也就是调用state的build方法;

 @override
  Widget build() => state.build(this);

2.2 setState如何更新widget?

如下是setState源码,其实关键就是这个markNeedsBuild,markNeedsBuild会标记_dirty表示有变更区域需更新,然后在下一次调用 WidgetsBinding.drawFrame绘制,其不是立即生效,是需要一次垂直同步信号VSync刷新的。

 void setState(VoidCallback fn) {
     ...
      _element.markNeedsBuild();
 }
 
 void markNeedsBuild() {
    ...
     if (dirty)
      return;
    _dirty = true;
    owner.scheduleBuildFor(this);
 }

在dirty设置为true时候,一般都是需要rebuild下widget的, ComponentElement#performRebuild-->Element#updateChild-->StatefulElement#update,

  void performRebuild() {
        ...
        _child = updateChild(_child, built, slot);
        ...
  }
  
  Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
       ...
       child.update(newWidget);
       ...
  }
  
   void update(StatefulWidget newWidget) {
    ...
    assert(widget == newWidget);
    final StatefulWidget oldWidget = _state._widget;
    _dirty = true;
    _state._widget = widget;
    ...
    rebuild();
   }

可以看到,其实_state从构造函数创建时候就不会被更新,在update方法时候只是更新侯的widget赋值给_state._widget,在rebuild触发一次Widget build() => state.build(this);

3 InheritedWidget如何传递子节点Model?

InheritedWidge表示子节点可继承一些父节点的状态;在抽象类Element中定义了_inheritedWidgets的Map集合类,InheritedElement继承Element,并且在_updateInheritance根据父节点是否有InheritedElement去初始化
_inheritedWidgets,如果父类没有则之间初始化,否则从父类获取;

abstract class Element extends DiagnosticableTree implements BuildContext {

    Map<Type, InheritedElement> _inheritedWidgets;
}
abstract class ProxyElement extends ComponentElement {
}

abstract class ComponentElement extends Element {
}

class InheritedElement extends ProxyElement {

    @override
  void _updateInheritance() {
    assert(_active);
    final Map<Type, InheritedElement> incomingWidgets = _parent?._inheritedWidgets;
    if (incomingWidgets != null)
      _inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);
    else
      _inheritedWidgets = HashMap<Type, InheritedElement>();
    _inheritedWidgets[widget.runtimeType] = this;
  }
}

_updateInheritance会在Element的mount和activate方法调用,两个方法都会在第一次加载时候触发;

void mount(Element parent, dynamic newSlot) {
  if (parent != null) // Only assign ownership if the parent is non-null
      _owner = parent.owner;
    if (widget.key is GlobalKey) {
      final GlobalKey key = widget.key;
      key._register(this);
    }
    _updateInheritance();
}

void activate() {
    _dependencies?.clear();
    _hadUnsatisfiedDependencies = false;
    _updateInheritance();
}

在比较的老的版本使用context.inheritFromWidgetOfExactType去获取父节点的model数据,但目前已经在v1.12.1.版本被废弃;

 @Deprecated(
    'Use dependOnInheritedWidgetOfExactType instead. '
    'This feature was deprecated after v1.12.1.'
  )
  @override
  InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
      ...
  }

新版本使用如下方式定义Model,如下:

 class MyModel extends InheritedModel<String> {

  static MyModel of(BuildContext context, String aspect) {
    return InheritedModel.inheritFrom<MyModel>(context, aspect: aspect);
  }
 }

接下来分析下InheritedModel.inheritFrom到底干了啥?

 static T inheritFrom<T extends    InheritedModel<Object>>(BuildContext context, { Object aspect }) {
   //1.
   if (aspect == null)
     return context.dependOnInheritedWidgetOfExactType<T>();

   // Create a dependency on all of the type T ancestor models up until
   // a model is found for which isSupportedAspect(aspect) is true.
   final List<InheritedElement> models = <InheritedElement>[];
   
   _findModels<T>(context, aspect, models);
   if (models.isEmpty) {
     return null;
   }
   final InheritedElement lastModel = models.last;
   for (InheritedElement model in models) {
     //2.
     final T value = context.dependOnInheritedElement(model, aspect: aspect);
     if (model == lastModel)
       return value;
   }

   assert(false);
   return null;
 }

标记1处,如果aspect没有指定,则之间从context.dependOnInheritedWidgetOfExactType获取InheritedWidget.

@override
 T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {
   assert(_debugCheckStateIsActiveForAncestorLookup());
   //这里的_inheritedWidgets在之前说过起初始化流程
   final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
   if (ancestor != null) {
     assert(ancestor is InheritedElement);
     return dependOnInheritedElement(ancestor, aspect: aspect);
   }
   _hadUnsatisfiedDependencies = true;
   return null;
 }
 
   @override
 InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
   assert(ancestor != null);
   _dependencies ??= HashSet<InheritedElement>();
   //添加依赖,这里很关键
   _dependencies.add(ancestor);
   ancestor.updateDependencies(this, aspect);
   return ancestor.widget;
 }

标记2处则直接调用dependOnInheritedElement;

@override
 InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
   assert(ancestor != null);
   _dependencies ??= HashSet<InheritedElement>();
   _dependencies.add(ancestor);
   ancestor.updateDependencies(this, aspect);
   return ancestor.widget;
 }

notifyClients调用是在widget的build时候会触发,其如下代码可以发现notifyClients就会 遍历满足_dependents的Element去触发回调notifyDependent,然后调用didChangeDependencies,最后markNeedsBuild触发更新。

  void notifyClients(InheritedWidget oldWidget) {
   assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
   for (Element dependent in _dependents.keys) {
     ...
     notifyDependent(oldWidget, dependent);
   }
 }
 
 void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
   dependent.didChangeDependencies();
 }
 
 void didChangeDependencies() {
   ...
   markNeedsBuild();
 }

因此总结如下:在使用InheritedModel.inheritFrom获取Model时候,其获取时候传的Context所代表的InheritedWidget会被登记到_dependencies中,一旦InheritedWidget被更新后,比如主题颜色,其之后的都会被触发notifyDependent,进而子节点会相应的触发markNeedsBuild。

相关文章

网友评论

    本文标题:状态State深入理解

    本文链接:https://www.haomeiwen.com/subject/kkcnihtx.html