FlutterFlutterFlutter

Flutter中Widget的生命周期和渲染原理

2020-06-19  本文已影响0人  TitanCoder
widget

FlutterWidget的生命周期

图片来源网络

上面图片大概列出了StatefulWidget的简单的函数调用过程

constructor

调用createState创建State对象时, 执行State类的构造方法(Constructor)来创建State对象

initState

  @override
  void initState() {
    // 这里必须调用super的方法
    super.initState();
    print('4. 调用_HomeScreenState----initState');
  }
  @protected
  @mustCallSuper
  void initState() {
    assert(_debugLifecycleState == _StateLifecycle.created);
  }

didChangeDependencies

  1. StatefulWidget第一次创建的时候didChangeDependencies会被调用一次, 会在initState方法之后会被立即调用
  2. 从其他对象中依赖一些数据发生改变时, 比如所依赖的InheritedWidget状态发生改变时, 也会被调用

build

didUpdateWidget

执行didUpdateWidget方法是在当父Widget触发重建时,系统会调用didUpdateWidget方法

dispose

setState

整个过程的代码如下:

class HomeScreen extends StatefulWidget {
  HomeScreen() {
    print('1. 调用HomeScreen---constructor');
  }
  @override
  _HomeScreenState createState() {
    print('2. 调用的HomeScreen---createState');
    return _HomeScreenState();
  }
}

class _HomeScreenState extends State<HomeScreen> {

  int _counter = 0;

  _HomeScreenState() {
    print('3. 调用_HomeScreenState----constructor');
  }

  @override
  void initState() {
    // 这里必须调用super的方法
    super.initState();
    print('4. 调用_HomeScreenState----initState');
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print('调用_HomeScreenState----didChangeDependencies');
  }
  
  @override
  Widget build(BuildContext context) {
    print('5. 调用_HomeScreenState----build');
    return Scaffold(
      appBar: AppBar(title: Text('生命周期', style: TextStyle(fontSize: 20))),
      body: Center(
        child: Column(
          children: <Widget>[
            Text('当前计数: $_counter', style: TextStyle(fontSize: 20),),
            RaisedButton(
              child: Text('点击增加计数', style: TextStyle(fontSize: 20),),
              onPressed: () {
                setState(() {
                  _counter++;
                });
              }
            )
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();

    print('6. 调用_HomeScreenState---dispose');
  }
}

打印结果如下:

flutter: 1. 调用HomeScreen---constructor
flutter: 2. 调用的HomeScreen---createState
flutter: 3. 调用_HomeScreenState----constructor
flutter: 4. 调用_HomeScreenState----initState
flutter: 调用_HomeScreenState----didChangeDependencies
flutter: 5. 调用_HomeScreenState----build

// 每次调用setState, 都会执行build
flutter: 5. 调用_HomeScreenState----build
flutter: 5. 调用_HomeScreenState----build

Flutter渲染原理

Flutter中渲染过程是通过Widget, ElementRenderObject实现的, 下面是FLutter中的三种树结构

Flutter

Widget

这是Flutter官网对Widget的说明

Flutter widgets are built using a modern framework that takes inspiration from React. The central idea is that you build your UI out of widgets. Widgets describe what their view should look like given their current configuration and state. When a widget’s state changes, the widget rebuilds its description, which the framework diffs against the previous description in order to determine the minimal changes needed in the underlying render tree to transition from one state to the next.

RenderObject

下面是Flutter官网对RenderObject的说明

An object in the render tree.

The RenderObject class hierarchy is the core of the rendering library's reason for being.

RenderObjects have a parent, and have a slot called parentData in which the parent RenderObject can store child-specific data, for example, the child position. The RenderObject class also implements the basic layout and paint protocols.

Element

下面是Flutter官网对Element的说明

An instantiation of a Widget at a particular location in the tree.

Widgets describe how to configure a subtree but the same widget can be used to configure multiple subtrees simultaneously because widgets are immutable. An Element represents the use of a widget to configure a specific location in the tree. Over time, the widget associated with a given element can change, for example, if the parent widget rebuilds and creates a new widget for this location.

Elements form a tree. Most elements have a unique child, but some widgets (e.g., subclasses of RenderObjectElement) can have multiple children.

对象的创建过程

Widget

// 继承关系Container --> StatelessWidget --> Widget
class Container extends StatelessWidget {
  @override
  Widget build(BuildContext context) {}
}

// 抽象类
abstract class StatelessWidget extends Widget {
  @override
  StatelessElement createElement() => StatelessElement(this);
  
  @protected
  Widget build(BuildContext context);
}

RenderObject

这里来看一下可渲染Widget的继承关系和相关源代码, 这里以Padding为例

// 继承关系: Padding --> SingleChildRenderObjectWidget --> RenderObjectWidget --> Widget
class Padding extends SingleChildRenderObjectWidget {
  @override
  RenderPadding createRenderObject(BuildContext context) {
    return RenderPadding(
      padding: padding,
      textDirection: Directionality.of(context),
    );
  }

  @override
  void updateRenderObject(BuildContext context, RenderPadding renderObject) {
    renderObject
      ..padding = padding
      ..textDirection = Directionality.of(context);
  }
}

abstract class SingleChildRenderObjectWidget extends RenderObjectWidget {
  @override
  SingleChildRenderObjectElement createElement() => SingleChildRenderObjectElement(this);
}

abstract class RenderObjectWidget extends Widget {
  @override
  RenderObjectElement createElement();

  @protected
  RenderObject createRenderObject(BuildContext context);

  @protected
  void updateRenderObject(BuildContext context, covariant RenderObject renderObject) { }

  @protected
  void didUnmountRenderObject(covariant RenderObject renderObject) { }
}
// 这里目的是为了创建一个RenderPadding
RenderPadding createRenderObject(BuildContext context) {
    return RenderPadding(
      padding: padding,
      textDirection: Directionality.of(context),
    );
}

上面这段代码中, 最终是创建了一个RenderPadding, 而这个RenderPadding又是什么呢? 下面看看他的继承关系和相关源代码

// 继承关系: RenderPadding --> RenderShiftedBox --> RenderBox --> RenderObject
class RenderPadding extends RenderShiftedBox {}

abstract class RenderShiftedBox extends RenderBox with RenderObjectWithChildMixin<RenderBox> {}

abstract class RenderBox extends RenderObject {}

RenderObject又是如何实现布局和渲染的呢

// 当外面修改padding时
RenderPadding createRenderObject(BuildContext context) {
    return RenderPadding(
      padding: padding,
      textDirection: Directionality.of(context),
    );
}

// RenderPadding类里面会调用padding属性的set方法
set padding(EdgeInsetsGeometry value) {
    if (_padding == value)
      // 如果传过来的值和之前的一样, 就不会被重新渲染, 直接return
      return;
    _padding = value;
    _markNeedResolution();
}

// 内部会调用markNeedsLayout
void _markNeedResolution() {
    _resolvedPadding = null;
    markNeedsLayout();
}

// 这里是RenderObject里面的一些核心方法
abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget {
  // markNeedsLayout是RenderObject类里面的方法
  // markNeedsLayout的目的就是标记在下一帧绘制时,需要重新布局performLayout
  void markNeedsLayout() {
    if (_needsLayout) {
      return;
    }
    
    if (_relayoutBoundary != this) {
      markParentNeedsLayout();
    } else {
      _needsLayout = true;
      if (owner != null) {
        owner._nodesNeedingLayout.add(this);
        owner.requestVisualUpdate();
      }
    }
  }

  // 
  void layout(Constraints constraints, { bool parentUsesSize = false }) {
    RenderObject relayoutBoundary;
    if (!parentUsesSize || sizedByParent || constraints.isTight || parent is! RenderObject) {
      relayoutBoundary = this;
    } else {
      relayoutBoundary = (parent as RenderObject)._relayoutBoundary;
    }
    if (!_needsLayout && constraints == _constraints && relayoutBoundary == _relayoutBoundary) {
      return;
    }
    _constraints = constraints;
    if (_relayoutBoundary != null && relayoutBoundary != _relayoutBoundary) {
      visitChildren(_cleanChildRelayoutBoundary);
    }
    _relayoutBoundary = relayoutBoundary;
    if (sizedByParent) {
      try {
        performResize();
      } catch (e, stack) {
        _debugReportException('performResize', e, stack);
      }
    }
    RenderObject debugPreviousActiveLayout;
    try {
      performLayout();
      markNeedsSemanticsUpdate();
    } catch (e, stack) {
      _debugReportException('performLayout', e, stack);
    }
    _needsLayout = false;
    markNeedsPaint();
  }
  
  // RenderObject还有一个可被子类重写的paint方法
  void paint(PaintingContext context, Offset offset) { }
}

Element

// 在Container的父类StatelessWidget中, 实例化了其父类的一个抽象方法
// 继承关系: StatelessElement --> ComponentElement --> Element
abstract class StatelessWidget extends Widget {
  // 实例化父类的抽象方法, 并把当前Widget作为参数传入了(this)
  @override
  StatelessElement createElement() => StatelessElement(this);
}

// 在Padding的父类SingleChildRenderObjectWidget中, 实例化了其父类的一个抽象方法
// 继承关系: SingleChildRenderObjectElement --> RenderObjectElement --> Element
abstract class SingleChildRenderObjectWidget extends RenderObjectWidget {
  
  // 实例化父类的抽象方法, 并把当前Widget作为参数传入了(this)
  @override
  SingleChildRenderObjectElement createElement() => SingleChildRenderObjectElement(this);
}
  /// Add this element to the tree in the given slot of the given parent.
  ///
  /// The framework calls this function when a newly created element is added to
  /// the tree for the first time. Use this method to initialize state that
  /// depends on having a parent. State that is independent of the parent can
  /// more easily be initialized in the constructor.
  ///
  /// This method transitions the element from the "initial" lifecycle state to
  /// the "active" lifecycle state.
  @mustCallSuper
  void mount(Element parent, dynamic newSlot) {
    _parent = parent;
    _slot = newSlot;
    _depth = _parent != null ? _parent.depth + 1 : 1;
    _active = true;
    if (parent != null) // Only assign ownership if the parent is non-null
      _owner = parent.owner;
    final Key key = widget.key;
    if (key is GlobalKey) {
      key._register(this);
    }
    _updateInheritance();
  }

StatelessElement

Container创建出来的是StatelessElement, 下面我们探索一下StatelessElement创建完成后, framework调用mount方法的过程, 这里只留下了相关核心代码

abstract class ComponentElement extends Element {
  @override
  void mount(Element parent, dynamic newSlot) {
    super.mount(parent, newSlot);
    
    _firstBuild();
  }

  void _firstBuild() {
    rebuild();
  }
  
  @override
  void performRebuild() {
    if (!kReleaseMode && debugProfileBuildsEnabled)
      Timeline.startSync('${widget.runtimeType}',  arguments: timelineWhitelistArguments);

    Widget built;
    try {
      // 这里调用的build方法, 当前类也没有实现, 所以还是只能到调用者(子类里面找该方法的实现)
      built = build();
      debugWidgetBuilderValue(widget, built);
    } catch (e, stack) {
      _debugDoingBuild = false;
      
    } finally {
      _dirty = false;
    }
    try {
      _child = updateChild(_child, built, slot);
      
    } catch (e, stack) {
      
      _child = updateChild(null, built, slot);
    }

    if (!kReleaseMode && debugProfileBuildsEnabled)
      Timeline.finishSync();
  }

  @protected
  Widget build();
}


abstract class Element extends DiagnosticableTree implements BuildContext {
  // 构造方法, 接收一个widget参数
  Element(Widget widget)
    : assert(widget != null),
      _widget = widget;

  @override
  Widget get widget => _widget;
  Widget _widget;
  
  void rebuild() {
    if (!_active || !_dirty)
      return;
      
    Element debugPreviousBuildTarget;
    
    // 这里调用的performRebuild方法, 在当前类并没有实现, 只能去自己的类里面查找实现
    performRebuild();
  }

  /// Called by rebuild() after the appropriate checks have been made.
  @protected
  void performRebuild();
}


class StatelessElement extends ComponentElement {
  // 这里的widget就是之前StatelessWidget中调用createElement创建element时传过来的this(widget)
  StatelessElement(StatelessWidget widget) : super(widget);

  @override
  StatelessWidget get widget => super.widget as StatelessWidget;

  // 这里的build方法就是拿到当前的widget, 并且调用自己的build方法
  @override
  Widget build() => widget.build(this);
}

上面的代码看着有点乱, 下面就理一下

  1. 这里我们创建的是StatelessElement, 在创建完一个Element之后,Framework会调用mount方法
  2. ComponentElement类中重写了mount方法, 所以framwork会调用这里的mount方法
  3. mount方法中直接调用的_firstBuild方法(第一次构建)
  4. _firstBuild方法又是直接调用的rebuild方法(重新构建)
  5. 然而在ComponentElement类中没有重写rebuild方法, 所以还是要调用父类的rebuild方法
  6. rebuild方法会调用performRebuild方法, 而且是调用ComponentElement内重写的performRebuild方法
  7. performRebuild方法内, 会调用build方法, 并用Widget类型的build接收返回值
  8. 而这个build方法在StatelessElement中的实现如下
  9. 也就是说, 在创建Element之后, 创建出来的elment会拿到传过来的widget, 然后调用widget自己的build方法, 这也就是为什么所有的Widget创建出来之后都会调用build方法的原因
Widget build() => widget.build(this);

所以在StatelessElement调用mount烦恼歌发最主要的作用就是挂在之后调用_firstBuild方法, 最终通过widget调用对应widgetbuild方法构建更多的东西

RenderObjectElement

abstract class SingleChildRenderObjectWidget extends RenderObjectWidget {
  // 这里是创建了一个SingleChildRenderObjectElement对象
  @override
  SingleChildRenderObjectElement createElement() => SingleChildRenderObjectElement(this);
}
/// 以下源码并不全, 这里只是拷贝了一些核心方法和相关源码
class SingleChildRenderObjectElement extends RenderObjectElement {
  // 同样构造函数接收一个widget参数
  SingleChildRenderObjectElement(SingleChildRenderObjectWidget widget) : super(widget);

  @override
  SingleChildRenderObjectWidget get widget => super.widget as SingleChildRenderObjectWidget;

  @override
  void mount(Element parent, dynamic newSlot) {
    super.mount(parent, newSlot);
    _child = updateChild(_child, widget.child, null);
  }
}


// RenderObjectElement类的相关实现
abstract class RenderObjectElement extends Element {
  // 构造函数接收一个widget参数
  RenderObjectElement(RenderObjectWidget widget) : super(widget);
  @override
  RenderObjectWidget get widget => super.widget as RenderObjectWidget;

  /// 创建一个RenderObject类型的变量
  @override
  RenderObject get renderObject => _renderObject;
  RenderObject _renderObject;


  @override
  void mount(Element parent, dynamic newSlot) {
    super.mount(parent, newSlot);
    
    // 在这里通过传过来的widget调用createRenderObject创建一个_renderObject
    _renderObject = widget.createRenderObject(this);
    _dirty = false;
  }
}

StatefulElement

class StatefulElement extends ComponentElement {
  /// 构造函数
  StatefulElement(StatefulWidget widget)
      : _state = widget.createState(),
        super(widget) {
    
    _state._element = this;
    
    _state._widget = widget;
  }

  State<StatefulWidget> get state => _state;
  State<StatefulWidget> _state;
}
class HomeScreen extends StatefulWidget {
  HomeScreen() {
    print('1. 调用HomeScreen---constructor');
  }
  @override
  _HomeScreenState createState() {
    return _HomeScreenState();
  }
}

class _HomeScreenState extends State<HomeScreen> {
  @override
  Widget build(BuildContext context) {
    return Container()
  }
}
@override
Widget build() => _state.build(this);

BuildContext

上面多次提到的build方法是有参数的, 而且不管是StatelessWidget还是State, 他们build方法的参数都是BuildContext

// StatelessWidget
abstract class StatelessWidget extends Widget {
  @protected
  Widget build(BuildContext context);
}

// State
@optionalTypeArgs
abstract class State<T extends StatefulWidget> with Diagnosticable {
  @protected
  Widget build(BuildContext context);
}

ComponentElement创建完成之后, 会调用mount方法, 最终都会调用对应的build方法

class StatelessElement extends ComponentElement {
  @override
  Widget build() => widget.build(this);
}

class StatefulElement extends ComponentElement {
  @override
  Widget build() => _state.build(this);
}
abstract class Element extends DiagnosticableTree implements BuildContext {}

小总结

StatelessElement

StatefulElement

RenderObjectElement

Widget的key

我们之前创建的每一个Widget, 在其构造方法中我们都会看到一个参数Key, name这个Key到底有何作用又何时使用呢

const Scaffold({ Key key, ... })
const Container({ Key key, ... })
const Text({ Key key, ... })

我们先看一个示例需求代码如下: 希望每次点击删除按钮删除数组的元素后, ListView中其他item的展示信息不变(包括颜色和字体)

class _HomeScreenState extends State<HomeScreen> {

  List<String> names = ["111111", "222222", "333333"];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Key Demo"),
      ),
      body: ListView(
        children: names.map((name) {
          return ListItemLess(name);
        }).toList(),
      ),

      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.delete),
        onPressed: () {
          setState(() {
            names.removeAt(0);
          });
        }
      ),
    );
  }
}

我们吧ListViewitem分别使用StatelessWidgetStatefulWidget实现, 看看两者区别

StatelessWidget

我们先对ListItem使用一个StatelessWidget进行实现:

class ListItemLess extends StatelessWidget {
  final String name;
  final Color randomColor = Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256));

  ListItemLess(this.name);

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 60,
      child: Text(name, style: TextStyle(fontSize: 30, color: Colors.white)),
      color: randomColor,
    );
  }
}

StatefulWidget

现在对ListItem使用StatefulWidget实现同样的功能

class ListItemFul extends StatefulWidget {
  final String name;
  ListItemFul(this.name): super();
  @override
  _ListItemFulState createState() => _ListItemFulState();
}

class _ListItemFulState extends State<ListItemFul> {
  final Color randomColor = Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256));

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 60,
      child: Text(widget.name),
      color: randomColor,
    );
  }
}
  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }

添加Key

在上面ListItemFul的基础上, 为每一个ListItemFul加上一个key

class ListItemFulKey extends StatefulWidget {
  final String name;
  ListItemFulKey(this.name, {Key key}): super(key: key);

  @override
  _ListItemFulKeyState createState() => _ListItemFulKeyState();
}


// 在上面使用的时候, 传入一个不同的key
ListItemFulKey(name, key: ValueKey(name))

Key的分类

@immutable
abstract class Key {
  /// 工厂构造函数
  const factory Key(String value) = ValueKey<String>;

  @protected
  const Key.empty();
}

abstract class LocalKey extends Key {
  /// Default constructor, used by subclasses.
  const LocalKey() : super.empty();
}

abstract class GlobalKey<T extends State<StatefulWidget>> extends Key { }

LocalKey

LocalKey有三个子类

ValueKey

ObjectKey

UniqueKey:

class ValueKey<T> extends LocalKey {
  const ValueKey(this.value);
}

class ObjectKey extends LocalKey {
  const ObjectKey(this.value);
}

class UniqueKey extends LocalKey {
  UniqueKey();
}

GlobalKey

class HomePage extends StatelessWidget {

  final GlobalKey<_HomeContentState> homeKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("GlobalKey Demo"),
      ),
      body: HomeContent(key: homeKey),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.delete),
        onPressed: () {
          final message = homeKey.currentState.message;
          final name = homeKey.currentState.widget.name;
          print('message = $message, name = $name');
          homeKey.currentState.newPrint();

          final currentCtx = homeKey.currentContext;
          print('currentCtx = $currentCtx');
        }
      ),
    );
  }
}

class HomeContent extends StatefulWidget {

  final String name = 'homeContent';

  HomeContent({ Key key }): super(key: key);

  @override
  _HomeContentState createState() => _HomeContentState();
}

class _HomeContentState extends State<HomeContent> {

  final String message = 'message';

  void newPrint() {
    print('new---print');
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

参考文档


欢迎您扫一扫下面的微信公众号,订阅我的博客!

微信公众号
上一篇 下一篇

猜你喜欢

热点阅读