原文在这里。git
本文详细解释了如何把Widget转化成了屏幕上的一个个像素点
想要成为一个更好的开发,了解底层的实现技术几乎是必不可少的。你能够更容易的建立自定义的布局和特效,若是你学习了这些底层技术是如何工做的。也可让少在电脑前加几个晚上的班。github
本文的目的就是要介绍Flutter之下的技术,并让你理解他们的是如何运行的。less
## 咱们如今开始ide
你也许已经知道如何使用StatelessWidget和StatefulWidget。可是这些Widget只是把其余的Widget组合到了一块儿。在屏幕加上的布局和绘制是发生在别的地方的。工具
**我强烈建议你打开你最心爱的编辑工具,而后跟着本文一步一步的查看实际代码是如何实现的并一路“原来如此”过去。、布局
## Opacity组件
从Opacity这个组件开始是最好不过了。这个Widget足够的简单,并且是一个绝佳的例子。学习
它只接受一个子组件。所以你能够把任何一个widget放进Opacity里改变其显示。另外还有一个值opacity
,在0.0和1.0之间。它用来控制透明度。动画
Opacity
是SingleChildRenderObjectWidget
的子类。继承的路径是这样的:ui
Opacity -> SingleChildRenderObjectWidget -> RenderObjectWidget -> Widget。
而StatelessWidget
和StatefulWidget
是这样的继承路径:this
StatelessWidget/StatefulWidget -> Widget
不一样之处就在于StatelessWidget和StatefulWidget只是把不一样的组件(Widget)组合在一块儿,而Opacity组件会控制一个组件如何绘制。
可是若是你想从Opacity的代码里找到一些绘制像素的线索,这几乎是徒劳的。这是由于一个Widget只包含了配置信息,好比Opacity
组件只包含了一个opacity的值。
这就是为何你能够在一个组件的
build
方法里建立新的Widget,里面并不会包含耗费资源的构建组件的代码,仅仅是包含了一些构建须要的信息。
绘制的时候会发生什么呢?
RenderObject
绘制都在RenderObject
里进行。这个单从名称里也能够知道了。Opacit组件这样建立和更新RenderObject:
@override RenderOpacity createRenderObject(BuildContext context) => new RenderOpacity(opacity: opacity); @override void updateRenderObject(BuildContext context, RenderOpacity renderObject) { renderObject.opacity = opacity; }
Opacity
组件把自身的size和子组件的size设定成同样的值。它基本上和它子组件的每一方面都同样,除了绘制。在绘制子组件以前添加了opacity值。
在这个状况下, RenderOpacity
须要实现全部的方法(好比,执行布局、碰撞检测和计算size)而后交给子类去执行具体的工做。
RenderOpacity
继承了RenderProxyBox
(这个类mixin了一些其余 的类)。这些类都分别实现了上面提到的那些方法。
double get opacity => _opacity; double _opacity; set opacity(double value) { _opacity = value; markNeedsPaint(); }
我(做者)删除了大部分的代码,要看所有代码能够在这里看。
getter把私有的值暴露出去。setter里面会调用markNeedsPaint()
或者markNeedsLayout()
。就如名字所言,它会通知系统“这个组件发生了改变,须要重绘或者从新布局”
在RenderOpacity
里面还有以下的代码:
@override void paint(PaintingContext context, Offset offset) { context.pushOpacity(offset, _alpha, super.paint); }
PaintingContext
就是一个画布。在这个画布上有一个方法叫作pushOpacity
!
这一行就是opacity的具体实现。
Opacity
不是一个StatelessWidget
或者StatefullWidget
而是一个SingleChildRendeObjectWidget
。Opacity
组件包含了一个opacity值。RenderOpacity
,继承自RenderProxyBox
,执行了具体的布局和绘制动做记住组件(widget)只不过是一个配置,RenderObject
才是管理布局和绘制的。
在Flutter里,你基本上一直都会建立新的Widget,当build()
方法被调用的时候你就建立了一大堆的组件。当某些变化发生的时候,build方法基本上都会被调用。好比一个动画里,build方法更会被常常调用。最好是不要每次都从新绘制整个子树,更新会更好一些。
你不能得到一个组件在屏幕上的大小或者位置,由于一个组件姿式一个蓝图,并非实际绘制在界面上的。它只包含了render object须要用的信息。
Element是一个组件树的实际组件。
第一次一个组件被建立的时候,会有一个Element被建立,而且二者互相关联。以后这个element被插入到了一个树里。若是组件(widget)发生了更改,它会和旧的组件对比,并对应的更新element。最重要的是在这个时候element不会从新建立,只会被更新。
Element是flutter核心的一部分,不过如今不须要知道更多的内容。这些就足够了。
请好奇的同窗看过来。在SingleChildRenderObectWidget
里
@override SingleChildRenderObjectElement createElement() => new SingleChildRenderObjectElement(this);
SingleChildRenderObjectElement
也只是一个有一个child的Element。
若是是Element建立了RenderObject,那么Opacity组件的RenderObject怎么是本身建立的呢?
基本上是由于Opacity组件只是须要一个RenderObject
可是不须要一个定制的Element。
看下面的代码:
SingleChildRenderObjectElement(SingleChildRenderObjectWidget widget) : super(widget);
SingleChildRenderObjectElement
有一个RenderObjectWidget
的引用(这里也包含了建立RenderObject
的方法)。
在RenderObjectElement#mount
方法里element被插入到了element树里:
@override void mount(Element parent, dynamic newSlot) { super.mount(parent, newSlot); _renderObject = widget.createRenderObject(this); attachRenderObject(newSlot); _dirty = false; }
在Element挂载的时候,它才会让组件建立一个render object。
这就是Opacity组件工做的原理。
个人目标是用这篇文章来介绍组件之下的原理。还有不少的内容没有覆盖到,可是我但愿这篇文章至少可让你初窥门径。