美文网首页
Flutter Widget 详解

Flutter Widget 详解

作者: 無名小子的杂货铺 | 来源:发表于2019-12-14 20:53 被阅读0次

Flutter 一切皆 Widget

Widget 树

Android View

我们知道在Android中,View 是屏幕上显示的所有内容的基础, 按钮、工具栏、输入框等一切都是View,同时也有ViewGroup 来容纳各种子 View 控件。

Flutter Widget

类比到 Flutter 中,Widget 相当于 Android 中的 View 概念,但是要比View 概括面更广,拿 Android 和 iOS 平台来举例,例如:Activity、ViewGroup、View、View Controller 、Theme、Application等,这些都是不同的类型,而在 Flutter 中这些都可以理解成 Widget,正是“Flutter 一些皆 Widget”。

不同点:

  • Android View 可以直接修改其属性
  • Flutter widget 每次更新需要重建widget

Widget VS Element

  • Widget:描述一个UI元素的配置数据,并不是最终绘制在设备屏幕上的显示元素,它只是描述显示元素的一个配置数据
  • Element:真实屏幕上显示元素,Widget只是描述Element的配置数据

一个Widget 可以对应多个Element 日常多操作的是Widget部分,当然可以自定义Widget 或者 Element

常见的Widget子类为StatelessWidget(无状态)和StatefulWidget(有状态);

  • StatelessWidget:内部没有保存状态,UI界面创建后不会发生改变
  • StatefulWidget:内部有保存状态,当状态发生改变,会触发更新

Widget 类

@immutable
abstract class Widget extends DiagnosticableTree {
  /// Initializes [key] for subclasses.
  const Widget({ this.key });

  /// Controls how one widget replaces another widget in the tree.
  ///
  /// If the [runtimeType] and [key] properties of the two widgets are
  /// [operator==], respectively, then the new widget replaces the old widget by
  /// updating the underlying element (i.e., by calling [Element.update] with the
  /// new widget). Otherwise, the old element is removed from the tree, the new
  /// widget is inflated into an element, and the new element is inserted into the
  /// tree.
  ///
  /// In addition, using a [GlobalKey] as the widget's [key] allows the element
  /// to be moved around the tree (changing parent) without losing state. When a
  /// new widget is found (its key and type do not match a previous widget in
  /// the same location), but there was a widget with that same global key
  /// elsewhere in the tree in the previous frame, then that widget's element is
  /// moved to the new location.
  ///
  /// Generally, a widget that is the only child of another widget does not need
  /// an explicit key.
  ///
  /// See also the discussions at [Key] and [GlobalKey].
  final Key key;

  /// Inflates this configuration to a concrete instance.
  ///
  /// A given widget can be included in the tree zero or more times. In particular
  /// a given widget can be placed in the tree multiple times. Each time a widget
  /// is placed in the tree, it is inflated into an [Element], which means a
  /// widget that is incorporated into the tree multiple times will be inflated
  /// multiple times.
  /// 核心接口
  @protected
  Element createElement();

  /// A short, textual description of this widget.
  @override
  String toStringShort() {
    return key == null ? '$runtimeType' : '$runtimeType-$key';
  }

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


  /// Whether the `newWidget` can be used to update an [Element] that currently
  /// has the `oldWidget` as its configuration.
  ///
  /// An element that uses a given widget as its configuration can be updated to
  /// use another widget as its configuration if, and only if, the two widgets
  /// have [runtimeType] and [key] properties that are [operator==].
  ///
  /// If the widgets have no key (their key is null), then they are considered a
  /// match if they have the same type, even if their children are completely
  /// different.
 /// 通过 oldWidget和newWidget 属性对比来决定是否重用 Element
  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }
}


Widget 类本身是一个抽象类,其中最核心的就是定义了createElement()接口,Widget 内部并没有什么复杂的处理,而在Flutter中,一般都不会直接继承 Widget 类来实现一个新 Widget。

Flutter 提供了两个实现类 StatelessWidget 或 StatefulWidget 。

StatelessWidget

abstract class StatelessWidget extends Widget {

  const StatelessWidget({ Key key }) : super(key: key);

  @override
  StatelessElement createElement() => StatelessElement(this);

  @protected
  Widget build(BuildContext context);
}

/// An [Element] that uses a [StatelessWidget] as its configuration.
class StatelessElement extends ComponentElement {
  /// Creates an element that uses the given widget as its configuration.
  StatelessElement(StatelessWidget widget) : super(widget);

  @override
  StatelessWidget get widget => super.widget;

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

  @override
  void update(StatelessWidget newWidget) {
    super.update(newWidget);
    assert(widget == newWidget);
    _dirty = true;
    rebuild();
  }
}

看上面代码 知道 StatelessElement 继承 ComponentElement,ComponentElement 又继承Element类,看注释知道 StatelessElement 与 StatelessWidget 相对应,并且 StatelessWidget 作为 StatelessElement 的配置数据。

其实知道这个过程就可以了,把概念和代码对应起来就容易理解了。

StatefulWidget

与 StatelessWidget 相对应的,有一些 Widget,比如: ListView 需要跟进网络请求的结果进行UI刷新,所以 StatefulWidget 不仅要 build UI,还有根据数据变化来更新UI,也就是重新绘制 Widget。

简单看下 StatefulWidget 代码

abstract class StatefulWidget extends Widget {
  const StatefulWidget({ Key key }) : super(key: key);

  @override
  StatefulElement createElement() => new StatefulElement(this);

  @protected
  State createState();
}

和 StatelessWidget 对比来看,创建了一个 StatefulElement,并且多了一个 createState 方法,少了一个 build 方法。我们知道 StatefulWidget 是一个有状态的 Widget,所以在内部上一 个 StatefulElement 对应一个 State 实例。

StatefulElement 的实现比 StatelessElement 复杂很多,代码就不贴了,主要看看有哪些方法:

我们只需要掌握两点

  • 如何更新 state 和 UI
  • 了解 Widget 的生命周期

掌握这两点之后才能更好的利用 StatelessWidget 和 StatefulWidget 实现更多的自定义 Widget。

有关 Widget 的生命周期下一篇会详细说明

代码实例

已传置 github flutter-free

相关文章

网友评论

      本文标题:Flutter Widget 详解

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