Image原理分析

_ImageState是Image所对应的State,也是图片加载的驱动者,它将通过ImageProvider的resolve方法获取一个ImageStream对象。

ImageStream负责提供图片信息的数据流,其对应的监听器由_ImageState的_getListener方法提供。而ImageStream的主要工作将委托给ImageStreamCompleter,因此其持有的ImageStreamListener也将传递给ImageStreamCompleter。图片信息真正的加载由ImageProvider的子类实现,例如NetworkImage将从网络加载图片,加载并解码后的图片信息ImageInfo交由ImageStreamCompleter进行通知。

PaintBinding将持有一个ImageCache实例,用于全局图片缓存的管理。

框架分析

_ImageState的didChangeDependencies方法发起图片的解析

Read More

Layer

Layer是Flutter中针对SceneBuilder的一些方法做的一个封装,每种Layer都对应了一个或多个SceneBuilder的方法。

Layer分类:

  • 有孩子节点的Layer:
    • OffsetLayer/TransformLayer:位移类
Read More

文本处理

TextPainter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

class MyPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
/// 绘制文本
const textStyle = TextStyle(color: Colors.black, fontSize: 24);
const textSpan = TextSpan(text: 'Hello, World', style: textStyle);
final textPainter = TextPainter(text: textSpan, textDirection: TextDirection.ltr);
textPainter.layout(minWidth: 0, maxWidth: size.width);
const offset = Offset(50, 100);
textPainter.paint(canvas, offset);
}

@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}

TextPainter封装了Paragraph,用于绘制文本。

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
class TextPainter {
TextPainter({
InlineSpan? text,//文本内容
TextAlign textAlign = TextAlign.start,//对齐方式
TextDirection? textDirection,
double textScaleFactor = 1.0,
int? maxLines,
String? ellipsis,
Locale? locale,
StrutStyle? strutStyle,
TextWidthBasis textWidthBasis = TextWidthBasis.parent,
ui.TextHeightBehavior? textHeightBehavior,
})


void layout({ double minWidth = 0.0, double maxWidth = double.infinity }) {
if (_paragraph != null && minWidth == _lastMinWidth && maxWidth == _lastMaxWidth) {
return;
}

if (_rebuildParagraphForPaint || _paragraph == null) {
_createParagraph();
}
_lastMinWidth = minWidth;
_lastMaxWidth = maxWidth;
// A change in layout invalidates the cached caret and line metrics as well.
_lineMetricsCache = null;
_previousCaretPosition = null;
_layoutParagraph(minWidth, maxWidth);
_inlinePlaceholderBoxes = _paragraph!.getBoxesForPlaceholders();
}

void _layoutParagraph(double minWidth, double maxWidth) {
_paragraph!.layout(ui.ParagraphConstraints(width: maxWidth));//布局
if (minWidth != maxWidth) {
double newWidth;
switch (textWidthBasis) {
case TextWidthBasis.longestLine:
newWidth = _applyFloatingPointHack(_paragraph!.longestLine);
case TextWidthBasis.parent:
newWidth = maxIntrinsicWidth;
}
newWidth = clampDouble(newWidth, minWidth, maxWidth);
if (newWidth != _applyFloatingPointHack(_paragraph!.width)) {
_paragraph!.layout(ui.ParagraphConstraints(width: newWidth));
}
}
}

void paint(Canvas canvas, Offset offset) {
final double? minWidth = _lastMinWidth;
final double? maxWidth = _lastMaxWidth;
if (_paragraph == null || minWidth == null || maxWidth == null) {
throw StateError(
'TextPainter.paint called when text geometry was not yet calculated.\n'
'Please call layout() before paint() to position the text before painting it.',
);
}

if (_rebuildParagraphForPaint) {
_createParagraph();
_layoutParagraph(minWidth, maxWidth);
}
canvas.drawParagraph(_paragraph!, offset);//绘制
}

ui.Paragraph? _paragraph; //封装的Paragraph

ui.Paragraph _createParagraph() {
final InlineSpan? text = this.text;
if (text == null) {
throw StateError('TextPainter.text must be set to a non-null value before using the TextPainter.');
}
final ui.ParagraphBuilder builder = ui.ParagraphBuilder(_createParagraphStyle());
text.build(builder, textScaleFactor: textScaleFactor, dimensions: _placeholderDimensions);
_inlinePlaceholderScales = builder.placeholderScales;
final ui.Paragraph paragraph = _paragraph = builder.build();
_rebuildParagraphForPaint = false;
return paragraph;
}


}

Read More

media_query

MediaQuery

MediaQuery 用于查询解析给定数据的媒体信息(例如,window宽高/横竖屏/像素密度比等信息)官方提供这个组件让开发者可以获取想要的数据。它主要用于不同尺寸大小设备的适配。

Object > DiagnosticableTree > Widget > ProxyWidget > InheritedWidget > MediaQuery

常用属性

data → MediaQueryData:MediaQueryData是MediaQuery.of获取数据的类型。

使用MediaQuery必须要MaterialApp 或者WidgetsApp去包裹我们的Widget,这样才能够提供正常使用它,否则会出现错误。

1
2
3
4
var deviceData = MediaQuery.of(context); // 返回 MediaQueryData
var width = deviceData.size.width; //返回context所在的窗口宽度
var height = deviceData.size.height;//返回context所在的窗口高度

Read More