Flutter widget, rendering和dart.ui的基本使用

Flutter渲染界面的架构,分三个层次:

其中:

  • dart.ui
    • 提供了最底层的渲染界面的封装
    • 主要包括一个Canvas类,以及围绕它的各种基本图形API,包括:点、线、矩形、路径、文本等等
    • dart.ui封装了和渲染引擎的交互
    • dart:ui library
  • rendering
    • 使用dart.ui的API,提供更高级更简便的渲染功能
    • 将界面抽象为一个RenderObject的树,渲染整个树将显示界面的内容
    • 树中的RenderObject是可变更的,变更后将触发新的渲染
    • 可以近似看作Web开发中的DOM
    • 见:rendering library
  • widget
    • 使用rendering API,提供更高级的抽象,便于开发可复用图形组件和界面
    • 可以创建一个Widget树生成界面,树中的Widget实例都是不可变的,更新Widget实质上是创建新的Widget,取代旧的
    • 一般情况下,开发Flutter主要是使用Widget,从这个层面上看,一切Flutter界面都是Widget
    • widgets library

下面我们用具体示例展示各个API的基本使用。



使用dart.ui#

示例代码见: https://github.com/MarshalW/flutter_examples/blob/helloworld_raw/lib/main.dart

运行效果:

代码比较繁琐,就不贴出来了,主要工作是:

  • 创建一个函数,绘制开始帧
  • 调用window对象,绘制开始帧,大致步骤如下:
    • 计算屏幕大小
    • 创建画布对象,大小设置为屏幕尺寸
    • 在画布上创建和绘制文本对象
    • 画布内容生成为图片对象
    • 图片通过window对象渲染



使用rendering#

示例代码见:https://github.com/MarshalW/flutter_examples/blob/helloworld_rendering/lib/main.dart

界面效果和上面一样。

代码得到了很大的简化,从34行减少到8行:

1
2
3
4
5
6
7
8
9
import 'package:flutter/rendering.dart';

void main() {
RenderingFlutterBinding(
root: RenderPositionedBox(
alignment: Alignment.center,
child: RenderParagraph(TextSpan(text: '你好,世界!'),
textDirection: TextDirection.ltr)));
}



使用Widget#

widget可以更少的代码。这里稍作调整,实现一个自定义的RederObjectWidget

代码见:https://github.com/MarshalW/flutter_examples/blob/custom_render_box/lib/main.dart

效果如下图:

一般情况下,我们使用的Widget都会继承自StatelessWidget或者StatefulWidget,或者对以存在的这些Widget做组合配置,很少需要自己绘制Widget

如果需要自己绘制Widget,需要继承RenderObjectWidget,或者它的子类。

本例中的Dots,继承自SingleChildRenderObjectWidget。实现绘制一个黑色的底,并在坐标200,450位置绘制一个蓝色的圆:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';

class Dots extends SingleChildRenderObjectWidget {
const Dots({Key key, Widget child}) : super(key: key, child: child);

@override
RenderObject createRenderObject(BuildContext context) {
return RenderDots();
}
}

class RenderDots extends RenderConstrainedBox {
RenderDots() : super(additionalConstraints: BoxConstraints.expand());

@override
void paint(PaintingContext context, Offset offset) {
Canvas canvas = context.canvas;

Paint paint = Paint()..color = const Color(0xff1a237e);
canvas.drawCircle(Offset(200, 450), 100.0, paint);
super.paint(context, offset);
}
}

void main() {
runApp(Directionality(
textDirection: TextDirection.ltr,
child: Dots(
child: Center(
child: Text('你好,世界!'),
),
)));
}

由示例代码可知:

  • 实现的Dots需要一个RenderObject实例,即RenderDots
  • RenderDots继承自RenderConstrainedBox,后者是RenderObject的子类,增加了对布局的支持
  • 覆盖RenderObjectpaint方法,可实现绘制界面的功能,使用的API来自dart.ui