【Flutter基础组件】Text详细讲解
前⾔
这⼀篇⽂章我来带⼤家⼀起来阅读⼀下flutter框架中的⽂本组件Text相关源码,对于⽂本组件Text的使⽤⼤家应该是⼗分的熟悉,但其内部实现机制应该是很少有⼈去了解。当你了解完text的内部实现后,你会发现你完全可以通过⾃定义⾃⼰的Text来实现⾃⼰的⽂本内容和布局⽅式。话不多说,让我们⼀起进⼊flutter世界,⼀睹Text组件的真正容貌吧。
第⼀节:Text之TextSpan树的形成
阅读过Text源码的⼈都应该知道Text最终是由TextSpan组成的树形结构,以下为Text类中的两个数据参数:
final String data;
final InlineSpan textSpan;
以下为构建“data”数据参数的构造函数:
const Text(
this.data, {
Key key,
this.style,
this.strutStyle,
this.locale,
this.softWrap,
this.overflow,
textstylethis.maxLines,
this.semanticsLabel,
}) :
textSpan = null,
super(key: key);
上图代码中可以看到该构造函数只能传⼊“data”作为必要的参数,并且“textSpan = null”。
const Text.rich(
Key key,
this.style,
this.strutStyle,
this.locale,
this.softWrap,
this.overflow,
this.maxLines,
this.semanticsLabel,
}) :
data = null,
super(key: key);
⽽上图代码中只能传⼊“textSpan”作为必要参数,并且“data = null”。
这⾥可以⼤概明了类Text只有两种构造⽅式:
1. 将参数“data”作为构造源,此时忽略了"textSpan"参数。
2. 将参数“textSpan"作为构造源,此时忽略了“data”参数。
我们再来看看Text类的“build“函数的主要逻辑:
@override
Widget build(BuildContext context) {
...
Widget result = RichText(
textAlign: textAlign ?? Align ?? TextAlign.start,
textDirection: textDirection, // RichText uses Directionality.of to obtain a default if this is null.
locale: locale, // RichText uses Localizations.localeOf to obtain a default if this is null
softWrap: softWrap ?? defaultTextStyle.softWrap,
overflow: overflow ?? defaultTextStyle.overflow,
textScaleFactor: textScaleFactor ?? ScaleFactorOf(context),
maxLines: maxLines ?? defaultTextStyle.maxLines,
strutStyle: strutStyle,
textWidthBasis: textWidthBasis ?? WidthBasis,
textHeightBehavior: textHeightBehavior ?? HeightBehavior ?? DefaultTextHeightBehavior.of(context),
text: TextSpan(
style: effectiveTextStyle,
text: data,
children: textSpan != null ? <InlineSpan>[textSpan] : null,
),
);
...
return result;
}
上图中我们主要观察"RichText"构造时传⼊的参数“text”,该text是⼀个"TextSpan"对象,该对象有两个主要的数据源:text - String类型,当类Text采⽤第⼀种传⼊“data”的构造⽅式时,该text才有值。
children - List<InlineSpan>?类型,当类Text采⽤第⼆种传⼊"textSpan"的构造⽅式时,该children才不会为null。从以上分析中得到这样的论述:
1. 当以“data”构造Text时,树形中只会存在⼀个根TextSpan。
2. 当以"textSpan"构造Text时,树形中会存在多个TextSpan⼦树结构。
我们具体看图:
1. 当以“data”构造Text时的树结构:
2. 当以"textSpan"构造Text时的树结构:
所以总结下来,不管Text是如何构建,内部总是会形成⼀棵TextSpan树。我们来看下⾯的例⼦:
Text.rich(
TextSpan(
text: "ABC\n",
children: [
TextSpan(
text: "DE\n",
children: [
TextSpan(
text: "F\n",
style: TextStyle(
fontSize: 12
)
)
]
),
TextSpan(
text: "HIJ\n",
style: TextStyle(
color:
),
children: [
TextSpan(
text: "K\n",
style: TextStyle(
color: Colors.black
)
),
TextSpan(
text: "LMN\n",
)
]
)
]
),
style: TextStyle(
fontSize: 24,
color: d
)
)
显⽰效果如下:
代码结合上图图⽚显⽰可知,代码中的顺序为从上往下来显⽰每个TextSpan数据,这是符合⽤户⾓度的。我们再来看该段代码演⽰的树形图:
从图形中的树状结构可知,每个TextSpan中都有“data”和"style"两个字段描述⽂本,其中“data”是⽂本字符内容,⽽“style”是⽤来修饰⽂本的显⽰风格的,我们称为⽂本样式。可以看到,如果⼦节点的⽂本样式为空或者样式中的某个属性没有,它就会继承⽗样式中的属性,这⾥采⽤的是就近原则,直到到根Root的样式。当然如果根样式中依然未到,就会使⽤默认值,像字体fontSize的默认值为14,颜⾊color的默认值为⿊⾊等等。
那么,对于这种树形特性,其内部是如何⼯作的呢?我们知道Text类的构建函数“build“最终返回的是RichText类对象,我们再深⼊RichText类代码中⼀探究竟:
class RichText extends MultiChildRenderObjectWidget {
......
@override
RenderParagraph createRenderObject(BuildContext context) {
assert(textDirection != null || debugCheckHasDirectionality(context));
return RenderParagraph(text,
textAlign: textAlign,
textDirection: textDirection ?? Directionality.of(context),
softWrap: softWrap,
overflow: overflow,
textScaleFactor: textScaleFactor,
maxLines: maxLines,
strutStyle: strutStyle,
textWidthBasis: textWidthBasis,
textHeightBehavior: textHeightBehavior,
locale: locale ?? Localizations.localeOf(context, nullOk: true),
);
}
......
}
可以看到RichText类创建的渲染对象为RenderParagraph,并且将TextSpan的根节点“text”传⼊,其余参数我们暂且不讨论。继续看
RenderParagraph类的关键代码:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论