Flutter底层技术:什么是Widget,RenderObject和Element?

2020-05-0814:09:01APP与小程序开发Comments2,542 views字数 3212阅读模式

你也许已经知道如何使用StatelessWidget和StatefulWidget。但是这些Widget只是把其他的Widget组合到了一起。在屏幕加上的布局和绘制是发生在别的地方的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

**我强烈建议你打开你最心爱的编辑工具,然后跟着本文一步一步的查看实际代码是如何实现的并一路“原来如此”过去。、文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

## Opacity组件
从Opacity这个组件开始是最好不过了。这个Widget足够的简单,而且是一个绝佳的例子。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

它只接受一个子组件。因此你可以把任何一个widget放进Opacity里改变其显示。另外还有一个值opacity,在0.0和1.0之间。它用来控制透明度。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

OpacitySingleChildRenderObjectWidget的子类。继承的路径是这样的:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

Opacity -> SingleChildRenderObjectWidget -> RenderObjectWidget -> Widget。

StatelessWidgetStatefulWidget是这样的继承路径:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

StatelessWidget/StatefulWidget -> Widget

不同之处就在于StatelessWidget和StatefulWidget只是把不同的组件(Widget)组合在一起,而Opacity组件会控制一个组件如何绘制。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

但是如果你想从Opacity的代码里找到一些绘制像素的线索,这几乎是徒劳的。这是因为一个Widget只包含了配置信息,比如Opacity组件只包含了一个opacity的值。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

这就是为什么你可以在一个组件的build方法里创建新的Widget,里面并不会包含耗费资源的构建组件的代码,仅仅是包含了一些构建需要的信息。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

绘制

绘制的时候会发生什么呢?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

RenderObject

绘制都在RenderObject里进行。这个单从名称里也可以知道了。Opacit组件这样创建和更新RenderObject:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

@override
RenderOpacity createRenderObject(BuildContext context) => new RenderOpacity(opacity: opacity);

@override
void updateRenderObject(BuildContext context, RenderOpacity renderObject) {
  renderObject.opacity = opacity;
}

RenderOpacity

Opacity组件把自身的size和子组件的size设定成一样的值。它基本上和它子组件的每一方面都一样,除了绘制。在绘制子组件之前添加了opacity值。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

在这个情况下, RenderOpacity需要实现所有的方法(比如,执行布局、碰撞检测和计算size)然后交给子类去执行具体的工作。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

RenderOpacity继承了RenderProxyBox(这个类mixin了一些其他 的类)。这些类都分别实现了上面提到的那些方法。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

double get opacity => _opacity;
double _opacity;
set opacity(double value) {
  _opacity = value;
  markNeedsPaint();
}

我(作者)删除了大部分的代码,要看全部代码可以在这里看。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

getter把私有的值暴露出去。setter里面会调用markNeedsPaint()或者markNeedsLayout()。就如名字所言,它会通知系统“这个组件发生了改变,需要重绘或者重新布局”文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

RenderOpacity里面还有如下的代码:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

@override
void paint(PaintingContext context, Offset offset) {
    context.pushOpacity(offset, _alpha, super.paint);
}

PaintingContext就是一个画布。在这个画布上有一个方法叫做pushOpacity文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

这一行就是opacity的具体实现。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

回顾

  • Opacity不是一个StatelessWidget或者StatefullWidget而是一个SingleChildRendeObjectWidget
  • 组件只包含了绘制的时候需要用到的信息。比如,Opacity组件包含了一个opacity值。
  • RenderOpacity,继承自RenderProxyBox,执行了具体的布局和绘制动作
  • 因为Opacity组件和它的子组件基本上完全一样,所以它代理了其子组件的所有方法
  • 它override了绘制(paint)方法,这个方法会给Opacity的子组件添加一个特定的opacity值

就这样了么?

记住组件(widget)只不过是一个配置,RenderObject才是管理布局和绘制的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

Flutter里,你基本上一直都会创建新的Widget,当build()方法被调用的时候你就创建了一大堆的组件。当某些变化发生的时候,build方法基本上都会被调用。比如一个动画里,build方法更会被经常调用。最好是不要每次都重新绘制整个子树,更新会更好一些。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

你不能获得一个组件在屏幕上的大小或者位置,因为一个组件姿势一个蓝图,并不是实际绘制在界面上的。它只包含了render object需要用的信息。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

Element

Element是一个组件树的实际组件。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

发生了什么

第一次一个组件被创建的时候,会有一个Element被创建,并且两者互相关联。之后这个element被插入到了一个树里。如果组件(widget)发生了更改,它会和旧的组件对比,并对应的更新element。最重要的是在这个时候element不会重新创建,只会被更新。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

Element是flutter核心的一部分,不过现在不需要知道更多的内容。这些就足够了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

Opacity组件的Element是在哪里创建的

请好奇的同学看过来。在SingleChildRenderObectWidget文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

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

SingleChildRenderObjectElement也只是一个有一个child的Element。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

Element创建RenderObject

如果是Element创建了RenderObject,那么Opacity组件的RenderObject怎么是自己创建的呢?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

基本上是因为Opacity组件只是需要一个RenderObject但是不需要一个定制的Element。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

看下面的代码:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

SingleChildRenderObjectElement(SingleChildRenderObjectWidget widget) : super(widget);

SingleChildRenderObjectElement有一个RenderObjectWidget的引用(这里也包含了创建RenderObject的方法)。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

RenderObjectElement#mount方法里element被插入到了element树里:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

@override
void mount(Element parent, dynamic newSlot) {
  super.mount(parent, newSlot);
  _renderObject = widget.createRenderObject(this);
  attachRenderObject(newSlot);
  _dirty = false;
}

在Element挂载的时候,它才会让组件创建一个render object。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

最后

这就是Opacity组件工作的原理。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

我的目标是用这篇文章来介绍组件之下的原理。还有很多的内容没有覆盖到,但是我希望这篇文章至少可以让你初窥门径。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/18615.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/xcx/18615.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定