Flutter 渲染树

基础知识

Flutter中我们往往会听到如下几个类:

  1. Widget
  2. Element
  3. RenderObject

我们来分别讨论下他们的作用:

Widget

用于描绘你的视图长什么,相当于一个建造楼房的蓝图。
内部存在函数用于生成ElementRenderObject.

class Widget{
  
 //生成Element
 Element createElement();
 
}
  
//Widget自类生成渲染器对象
class RenderObjectWidget extends Widget {
	RenderObject createRenderObject(BuildContext context);
}

RenderObject

Widget设计绘制到屏幕中,再往下便是底层绘制引擎,这个对象被Element持有引用。

Element

持有WidgetRenderObject,决定Widget描绘的层次与提高绘制性能。可以简单理解这个类用RenderObject来渲染所关联的Widget,并且内部有优化机制决定是否更新渲染。

WidgetTree

Flutter视图由一个个Widget组合而来得到最终的渲染蓝图。

如下图:
在这里插入图片描述

ElementTree

每一个Widget会生成对应的Element,最后这些生成的Element也会形成一个树。
在这里插入图片描述

RenderTree

在这里插入图片描述

Tip: 部分Widget没有RenderObject对象,而是来自子树的。读者可自行深入

简单抽象关系图

Element会持有两个对象的引用:
在这里插入图片描述
在这里插入图片描述

如果WidgetStatefulWidget 那么Element还会持有对应的State

在这里插入图片描述

绘制极简流程

StatefulWidget绘制

StatefulWidget绘制设计蓝图是位于State类的build函数返回。

class MyStateWidget extends StatefulWidget {
  @override
  _MyStateWidgetState createState() => _MyStateWidgetState();
}
class _MyStateWidgetState extends State<MyStateWidget> {
  //决定绘制的蓝图
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

绘制教程:

StatefulWidget系统会调用createElement生成一个Element
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

在这里插入图片描述
在这里插入图片描述

tip: 以上顺序笔者并没有严格阅读源码,可能存在错误请注意!! - -其实是不想看

StatelessWidget绘制

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

小结

我们的StatelessWidgetStatefulWidget中的build函数的返回的上下文就是element.

StatelessWidget自己的build函数返回视图的配置信息给Render对象进行绘制。

StatefulWidget 使用状态类state的build返回的视图配置给Render对象进行绘制

Widget的key

Widget存一个属性key,在构造时可以传入。默认null.
key主要的作用的是用来判断当 WidgetTree发生了移动删除后,ElementTree对应的Element是否需要更新对Widget的引用,或移动某个ElementWidgetTree的位置,以及是否销毁重建。具体可以看 参考1

GlobalKey和LocalKey的区别

仅讨论StatefulWidget

LocalKey

当两个Widget在同一个父级时且做一次交换位置,那么element不会删除重建,只会根据状态更新引用或者交换ElementTree的元素位置,如果两个Widget不属于同一个父级那么会删除Element节点重建。

如下例子:

点击按钮交换两个Widget的位置(UniqueKeyLocalKey子类)
在这里插入图片描述

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

import 'UniqueColorGenerator.dart';

void main() => runApp(new MaterialApp(home: PositionedTiles()));

class PositionedTiles extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => PositionedTilesState();
}

class PositionedTilesState extends State<PositionedTiles> {

  List<Widget> tiles = [
    Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        children: [StatefulColorfulTile(key: UniqueKey())],
      ),
    ),
    Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        children: [StatefulColorfulTile(key: UniqueKey())],
      ),
    ),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(children: tiles),
      floatingActionButton: FloatingActionButton(
          child: Icon(Icons.sentiment_very_satisfied), onPressed: swapTiles),
    );
  }

  swapTiles() {
    setState(() {
      tiles.insert(1, tiles.removeAt(0));
    });
  }
}


class StatelessColorfulTile extends StatelessWidget {
  Color myColor = UniqueColorGenerator.getColor();

  @override
  Widget build(BuildContext context) {
    debugPrint(
        "StatelessColorfulTile ${this.hashCode} 重新build element ${context.hashCode} ${this.createElement() == context}");

    return Container(
        margin: EdgeInsets.only(top: 50),
        color: myColor,
        child: Padding(padding: EdgeInsets.all(70.0)));
  }
}

class StatefulColorfulTile extends StatefulWidget {
  StatefulColorfulTile({Key key}) : super(key: key); // NEW CONSTRUCTOR

  @override
  ColorfulTileState createState() {
    debugPrint(
        "StatefulColorfulTile ${this.hashCode} 重新createState");
   return ColorfulTileState();
  }
}


class ColorfulTileState extends State<StatefulColorfulTile> {
  Color myColor;

  @override
  void initState() {
    super.initState();
    myColor = UniqueColorGenerator.getColor();
  }

  @override
  Widget build(BuildContext context) {
    debugPrint(
        "ColorfulTileState ${this.hashCode} 重新build element ${context.hashCode}   widget: ${widget.hashCode}");
//
    return Container(
        color: myColor,
        child: Padding(
          padding: EdgeInsets.all(70.0),
        ));
  }
}

多次点击按钮的日志:

flutter: StatefulColorfulTile 731559728 重新createState
flutter: ColorfulTileState 940397615 重新build element 202   widget: 731559728
flutter: StatefulColorfulTile 318147042 重新createState
flutter: ColorfulTileState 967144222 重新build element 206   widget: 318147042
flutter: StatefulColorfulTile 318147042 重新createState
flutter: ColorfulTileState 54916526 重新build element 210   widget: 318147042
flutter: StatefulColorfulTile 731559728 重新createState
flutter: ColorfulTileState 734210995 重新build element 214   widget: 731559728

可见element被多次创建


GlobalKey

不论两个Widget是否是同一个父级,当Widget交换位置的时候,Element都会复用。
我们把上述代码做下简单修改:

 List<Widget> tiles = [
    Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        children: [StatefulColorfulTile(key: GlobalKey())],
      ),
    ),
    Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        children: [StatefulColorfulTile(key: GlobalKey())],
      ),
    ),
  ];

多次点击:

flutter: ColorfulTileState 11684886 重新build element 117   widget: 470363955
flutter: ColorfulTileState 11684886 重新build element 117   widget: 470363955
flutter: ColorfulTileState 291476133 重新build element 111   widget: 1022701588
flutter: ColorfulTileState 11684886 重新build element 117   widget: 470363955
flutter: ColorfulTileState 291476133 重新build element 111   widget: 1022701588
flutter: ColorfulTileState 291476133 重新build element 111   widget: 1022701588
flutter: ColorfulTileState 11684886 重新build element 117   widget: 470363955

可见element 没有创建新的,而是移动的了位置

参考

1. Keys! What are they good for?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值