Flutter开发进阶之瞧瞧Widget

本文详细介绍了Flutter中的Widget基础概念,包括其不可变性、树形结构、有状态与无状态的区别,以及StatefulWidget中的State管理和Element的角色。
摘要由CSDN通过智能技术生成

Flutter开发进阶之瞧瞧Widget

在Flutter开发中,WIdget是构建界面的基本单元;Widget是不可变的,意味着一旦创建如果需要改变UI就需要重新创建一个新的Widget;在实际开发中,Widget通常由一个个Widget组合而成,从而形成嵌套的树形结构,复杂的UI就是由这一个个Widget构建而成;Widget分为有状态和无状态,无状态Stateless根据输入参数渲染UI,有状态Stateful可以管理自己的状态并重建UI。
这篇文章将通过解析Widget的构成去理解Widget。
Flutter开发进阶之瞧瞧Widget

Widget


abstract class Widget extends DiagnosticableTree {
  const Widget({ this.key });
  
  final Key? key;
  
  
  
  Element createElement();
  
  
  String toStringShort() {
    final String type = objectRuntimeType(this, 'Widget');
    return key == null ? type : '$type-$key';
  }

  
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
  }

  
  
  bool operator ==(Object other) => super == other;

  
  
  int get hashCode => super.hashCode;

  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }

  static int _debugConcreteSubtype(Widget widget) {
    return widget is StatefulWidget ? 1 :
           widget is StatelessWidget ? 2 :
           0;
  }
}

以上是系统源码,可以看到Widget本身是@immutable,意思是不可变的,这与我们的猜想一致。
Widget继承自DiagnosticableTree说明Widget允许以树形组织呈现诊断信息,DiagnosticableTree混合了Diagnosticable的抽象类,它是Flutter框架用于调试和诊断的工具之一,通过DiagnosticableTree可以方便查看和理解应用内部状态以及问题的根源。
Widget默认带有属性key,通过查看Key的源码,以下。


abstract class Key {

  const factory Key(String value) = ValueKey<String>;

  
  const Key.empty();
}

同上,Key也是不可变的,默认的Key为ValueKey ,通过查看ValueKey的源码,以下。

class ValueKey<T> extends LocalKey {

  const ValueKey(this.value);

  final T value;

  
  bool operator ==(Object other) {
    if (other.runtimeType != runtimeType) {
      return false;
    }
    return other is ValueKey<T>
        && other.value == value;
  }

  
  int get hashCode => Object.hash(runtimeType, value);

  
  String toString() {
    final String valueString = T == String ? "<'$value'>" : '<$value>';
    if (runtimeType == _TypeLiteral<ValueKey<T>>().type) {
      return '[$valueString]';
    }
    return '[$T $valueString]';
  }
}

ValueKey继承自LocalKey,继续查看LocalKey,以下。

abstract class LocalKey extends Key {

  const LocalKey() : super.empty();
}

发现LocalKey继承自Key,完成闭环。
继续向下解析,每个Widget会对应一个Element,这是基本单元对应的实例,通过createElement创建,通过查看源码abstract class Element extends DiagnosticableTree implements BuildContext可知Element即为BuildContext。
继续向下解析,Widget的刷新机制通过比较新旧Widget的runtimeType和key去作为标准。
继续解析Widget就不得不查看Element,代码实在有些长,就不贴了。

StatelessWidget

abstract class StatelessWidget extends Widget {

  const StatelessWidget({ super.key });

  
  StatelessElement createElement() => StatelessElement(this);

  
  Widget build(BuildContext context);
}

StatelessWidget继承自Widget,createElement会将Widget作为参数传递给StatelessElement的初始化方法,通过查看StatelessElement源码,以下。

class StatelessElement extends ComponentElement {

  StatelessElement(StatelessWidget super.widget);

  
  Widget build() => (widget as StatelessWidget).build(this);

  
  void update(StatelessWidget newWidget) {
    super.update(newWidget);
    assert(widget == newWidget);
    rebuild(force: true);
  }
}

联系起来,createElement会将Widget交给Element,Element的update会调用Widget的canUpdate,最终会判断调用build,build会将Element自身作为BuildContext交给StatelessWidget。

StatefulWidget

abstract class StatefulWidget extends Widget {

  const StatefulWidget({ super.key });

  
  StatefulElement createElement() => StatefulElement(this);

  
  
  State createState();
}

StatefulWidget相对而言多一个State<StatefulWidget>? _state,在StatefulElement的初始化方法中,以下。

StatefulElement(StatefulWidget widget)
      : _state = widget.createState(),
        super(widget) {
    assert(() {
      if (!state._debugTypesAreRight(widget)) {
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('StatefulWidget.createState must return a subtype of State<${widget.runtimeType}>'),
          ErrorDescription(
            'The createState function for ${widget.runtimeType} returned a state '
            'of type ${state.runtimeType}, which is not a subtype of '
            'State<${widget.runtimeType}>, violating the contract for createState.',
          ),
        ]);
      }
      return true;
    }());
    assert(state._element == null);
    state._element = this;
    assert(
      state._widget == null,
      'The createState function for $widget returned an old or invalid state '
      'instance: ${state._widget}, which is not null, violating the contract '
      'for createState.',
    );
    state._widget = widget;
    assert(state._debugLifecycleState == _StateLifecycle.created);
  }

会调用Widget的createState方法。
继续查看StatefulElement源码,以下。

class StatefulElement extends ComponentElement {
  /// Creates an element that uses the given widget as its configuration.
  StatefulElement(StatefulWidget widget)
      : _state = widget.createState(),
        super(widget) {
    assert(() {
      if (!state._debugTypesAreRight(widget)) {
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('StatefulWidget.createState must return a subtype of State<${widget.runtimeType}>'),
          ErrorDescription(
            'The createState function for ${widget.runtimeType} returned a state '
            'of type ${state.runtimeType}, which is not a subtype of '
            'State<${widget.runtimeType}>, violating the contract for createState.',
          ),
        ]);
      }
      return true;
    }());
    assert(state._element == null);
    state._element = this;
    assert(
      state._widget == null,
      'The createState function for $widget returned an old or invalid state '
      'instance: ${state._widget}, which is not null, violating the contract '
      'for createState.',
    );
    state._widget = widget;
    assert(state._debugLifecycleState == _StateLifecycle.created);
  }

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

  State<StatefulWidget> get state => _state!;
  State<StatefulWidget>? _state;

  
  void reassemble() {
    if (_debugShouldReassemble(_debugReassembleConfig, _widget)) {
      state.reassemble();
    }
    super.reassemble();
  }

  
  void _firstBuild() {
    assert(state._debugLifecycleState == _StateLifecycle.created);
    final Object? debugCheckForReturnedFuture = state.initState() as dynamic;
    assert(() {
      if (debugCheckForReturnedFuture is Future) {
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('${state.runtimeType}.initState() returned a Future.'),
          ErrorDescription('State.initState() must be a void method without an `async` keyword.'),
          ErrorHint(
            'Rather than awaiting on asynchronous work directly inside of initState, '
            'call a separate method to do this work without awaiting it.',
          ),
        ]);
      }
      return true;
    }());
    assert(() {
      state._debugLifecycleState = _StateLifecycle.initialized;
      return true;
    }());
    state.didChangeDependencies();
    assert(() {
      state._debugLifecycleState = _StateLifecycle.ready;
      return true;
    }());
    super._firstBuild();
  }

  
  void performRebuild() {
    if (_didChangeDependencies) {
      state.didChangeDependencies();
      _didChangeDependencies = false;
    }
    super.performRebuild();
  }

  
  void update(StatefulWidget newWidget) {
    super.update(newWidget);
    assert(widget == newWidget);
    final StatefulWidget oldWidget = state._widget!;
    state._widget = widget as StatefulWidget;
    final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic;
    assert(() {
      if (debugCheckForReturnedFuture is Future) {
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('${state.runtimeType}.didUpdateWidget() returned a Future.'),
          ErrorDescription( 'State.didUpdateWidget() must be a void method without an `async` keyword.'),
          ErrorHint(
            'Rather than awaiting on asynchronous work directly inside of didUpdateWidget, '
            'call a separate method to do this work without awaiting it.',
          ),
        ]);
      }
      return true;
    }());
    rebuild(force: true);
  }

  
  void activate() {
    super.activate();
    state.activate();
    
    assert(_lifecycleState == _ElementLifecycle.active); 
    markNeedsBuild();
  }

  
  void deactivate() {
    state.deactivate();
    super.deactivate();
  }

  
  void unmount() {
    super.unmount();
    state.dispose();
    assert(() {
      if (state._debugLifecycleState == _StateLifecycle.defunct) {
        return true;
      }
      throw FlutterError.fromParts(<DiagnosticsNode>[
        ErrorSummary('${state.runtimeType}.dispose failed to call super.dispose.'),
        ErrorDescription(
          'dispose() implementations must always call their superclass dispose() method, to ensure '
          'that all the resources used by the widget are fully released.',
        ),
      ]);
    }());
    state._element = null;
    _state = null;
  }

  
  InheritedWidget dependOnInheritedElement(Element ancestor, { Object? aspect }) {
    assert(() {
      final Type targetType = ancestor.widget.runtimeType;
      if (state._debugLifecycleState == _StateLifecycle.created) {
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('dependOnInheritedWidgetOfExactType<$targetType>() or dependOnInheritedElement() was called before ${state.runtimeType}.initState() completed.'),
          ErrorDescription(
            'When an inherited widget changes, for example if the value of Theme.of() changes, '
            "its dependent widgets are rebuilt. If the dependent widget's reference to "
            'the inherited widget is in a constructor or an initState() method, '
            'then the rebuilt dependent widget will not reflect the changes in the '
            'inherited widget.',
          ),
          ErrorHint(
            'Typically references to inherited widgets should occur in widget build() methods. Alternatively, '
            'initialization based on inherited widgets can be placed in the didChangeDependencies method, which '
            'is called after initState and whenever the dependencies change thereafter.',
          ),
        ]);
      }
      if (state._debugLifecycleState == _StateLifecycle.defunct) {
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('dependOnInheritedWidgetOfExactType<$targetType>() or dependOnInheritedElement() was called after dispose(): $this'),
          ErrorDescription(
            'This error happens if you call dependOnInheritedWidgetOfExactType() on the '
            'BuildContext for a widget that no longer appears in the widget tree '
            '(e.g., whose parent widget no longer includes the widget in its '
            'build). This error can occur when code calls '
            'dependOnInheritedWidgetOfExactType() from a timer or an animation callback.',
          ),
          ErrorHint(
            'The preferred solution is to cancel the timer or stop listening to the '
            'animation in the dispose() callback. Another solution is to check the '
            '"mounted" property of this object before calling '
            'dependOnInheritedWidgetOfExactType() to ensure the object is still in the '
            'tree.',
          ),
          ErrorHint(
            'This error might indicate a memory leak if '
            'dependOnInheritedWidgetOfExactType() is being called because another object '
            'is retaining a reference to this State object after it has been '
            'removed from the tree. To avoid memory leaks, consider breaking the '
            'reference to this object during dispose().',
          ),
        ]);
      }
      return true;
    }());
    return super.dependOnInheritedElement(ancestor as InheritedElement, aspect: aspect);
  }

  bool _didChangeDependencies = false;

  
  void didChangeDependencies() {
    super.didChangeDependencies();
    _didChangeDependencies = true;
  }

  
  DiagnosticsNode toDiagnosticsNode({ String? name, DiagnosticsTreeStyle? style }) {
    return _ElementDiagnosticableTreeNode(
      name: name,
      value: this,
      style: style,
      stateful: true,
    );
  }

  
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<State<StatefulWidget>>('state', _state, defaultValue: null));
  }
}

StatefulElement通过持有state并对其生命周期进行管理。

  • 36
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kevin写代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值