大家在學習 Flutter 時一定會用過 Text,而對于一些複雜文本的處理可能會選擇 RichText ,再進一步,使用 RichText 就一定要用 TextSpan
,小菜本以為可以做為一個小知識點進行簡單學習,但是随着深入嘗試發現 TextSpan 涉及東西很多,很值得研究,是以單獨整理一篇小博文。
RichText 富文本核心即 TextSpan,而 TextSpan 結構很像 Android 中的 ViewGroup 樹型結構。
RichText 日常用法
小菜了解為 RichText 是進階版的 Text,如下直接看執行個體:
- TextDirection 用來控制文字位置,居左或居右邊;當與 TextAlign 屬性共存時,優先看整體,以 TextAlign 為準;
Widget richTextWid01() {
return RichText(
text: TextSpan(
text: 'TextDirection.ltr 文字預設居左',
style: TextStyle(fontSize: 16.0, color: Colors.black)),
textDirection: TextDirection.ltr);
}
Widget richTextWid02() {
return RichText(
text: TextSpan(
text: 'TextDirection.rtl 文字預設居右',
style: TextStyle(fontSize: 16.0, color: Colors.black)),
textDirection: TextDirection.rtl);
}
Widget richTextWid03() {
return RichText(
text: TextSpan(
text: 'textDirection 與 textAlign 同時設定,優先看整體,文字居中',
style: TextStyle(fontSize: 16.0, color: Colors.black)),
textDirection: TextDirection.rtl,
textAlign: TextAlign.center);
}
- RichText 可以借助 TextSpan 實作文字的多種效果,小菜認為有點像文字效果拼接,每個 TextSpan 可以設定單獨效果;
Widget richTextWid04() {
return RichText(
text: TextSpan(
text: '多種樣式,如:',
style: TextStyle(fontSize: 16.0, color: Colors.black),
children: <TextSpan>[
TextSpan(
text: '紅色',
style: TextStyle(fontSize: 18.0, color: Colors.red)),
TextSpan(
text: '綠色',
style: TextStyle(fontSize: 18.0, color: Colors.green)),
TextSpan(
text: '藍色',
style: TextStyle(fontSize: 18.0, color: Colors.blue)),
TextSpan(
text: '白色',
style: TextStyle(fontSize: 18.0, color: Colors.white)),
TextSpan(
text: '紫色',
style: TextStyle(fontSize: 18.0, color: Colors.purple)),
TextSpan(
text: '黑色',
style: TextStyle(fontSize: 18.0, color: Colors.black))
]),
textAlign: TextAlign.center);
}
- TextSpan 可以借助 recognizer 設定點選事件,包括點選/長按等;
final TapGestureRecognizer recognizer = TapGestureRecognizer();
void initState() {
super.initState();
recognizer.onTap = () {
Toast.show('您好,歡迎點贊或關注!', context,
duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM);
};
}
Widget richTextWid05() {
return RichText(
text: TextSpan(
text: 'recognizer 為手勢識别者,可設定點選事件,',
style: TextStyle(fontSize: 17.0, color: Colors.black),
children: <TextSpan>[
TextSpan(
text: '點我試試',
style: TextStyle(fontSize: 17.0, color: Colors.blue),
recognizer: recognizer)
]));
}
TextPainter 日常用法
RichText 的使用很友善,但如果在深入了解 TextSpan 就有很多趣味了;Flutter 提供了和 Android 類似的 Canvas 繪制方法,但是 Canvas 卻不支援 drawText,如果想要實作繪制文字,就需要
TextPainter而其内部主要是由 TextSpan 實作。
使用 TextPainter 時需要繼承 CustomPainter,并實作 paint 和 shouldRepaint 方法,主要是在 paint 中進行繪制 TextPainter。與 RichText 功能相同,可以完全實作 RichText 效果;
TextPainter 繪制需要實作 layout 與 paint 方法,即繪制位置與繪制範圍。
- TextDirection 和 TextAlign 效果與 RichText 一緻,但是 TextPainter 繪制時需要設定 layout 的最大最小範圍,而此時,文字位置與 layout 有關;當文字長度小于設定的 minWidth 最小寬度時,以 minWidth 寬度為限制居左/居右/居中等;而當文字長度大于設定的 minWidth 最小寬度時,以 maxWidth 最大寬度為限制,包括換行等;
TextPainter(
text: TextSpan(
text: 'TextDirection.ltr 文字預設居左',
style: TextStyle(fontSize: 16.0, color: Colors.black)),
textDirection: TextDirection.ltr)
..layout(maxWidth: Screen.width, minWidth: Screen.width)
..paint(canvas, Offset(0.0, 0.0));
TextPainter(
text: TextSpan(
text: 'TextDirection.rtl 文字預設居右',
style: TextStyle(fontSize: 16.0, color: Colors.black)),
textDirection: TextDirection.rtl)
..layout(maxWidth: Screen.width, minWidth: Screen.width)
..paint(canvas, Offset(0.0, 24.0));
TextPainter(
text: TextSpan(
text: 'textDirection 與 textAlign 同時設定,優先看整體,文字居中',
style: TextStyle(fontSize: 16.0, color: Colors.black)),
textDirection: TextDirection.rtl,
textAlign: TextAlign.center)
..layout(maxWidth: Screen.width, minWidth: Screen.width)
..paint(canvas, Offset(0.0, 48.0));
TextPainter(
text: TextSpan(
text: '文字位置與 layout 的最大最小寬度有關',
style: TextStyle(fontSize: 16.0, color: Colors.black)),
textDirection: TextDirection.rtl)
..layout(maxWidth: Screen.width - 100.0, minWidth: Screen.width - 200.0)
..paint(canvas, Offset(0.0, 90.0));
TextPainter(
text: TextSpan(
text: '文字長度較小',
style: TextStyle(fontSize: 16.0, color: Colors.black)),
textDirection: TextDirection.rtl)
..layout(maxWidth: Screen.width - 100.0, minWidth: Screen.width - 140.0)
..paint(canvas, Offset(0.0, 124.0));
- 而對于繪制多效果的樣式也是很友善,與 RichText 基本一緻;
TextPainter(
text: TextSpan(
text: '多種樣式,如:',
style: TextStyle(fontSize: 16.0, color: Colors.black),
children: <TextSpan>[
TextSpan(
text: '紅色',
style: TextStyle(fontSize: 18.0, color: Colors.red)),
TextSpan(
text: '綠色',
style: TextStyle(fontSize: 18.0, color: Colors.green)),
TextSpan(
text: '藍色',
style: TextStyle(fontSize: 18.0, color: Colors.blue)),
TextSpan(
text: '白色',
style: TextStyle(fontSize: 18.0, color: Colors.white)),
TextSpan(
text: '\n紫色',
style: TextStyle(fontSize: 18.0, color: Colors.purple)),
TextSpan(
text: '黑色',
style: TextStyle(fontSize: 18.0, color: Colors.black))
]),
textDirection: TextDirection.ltr,
textAlign: TextAlign.center)
..layout(maxWidth: Screen.width, minWidth: Screen.width)
..paint(canvas, Offset(0.0, 148.0));
- 小菜一直有問題的就是設定點選事件,小菜以為與 RichText 一樣直接傳遞 recognizer 即可,但始終無法調起,希望有解決過這個問題的朋友多多指導,如下是小菜的測試代碼;
TextPainter(
text: TextSpan(
text: 'recognizer 為手勢識别者,可設定點選事件,',
style: TextStyle(fontSize: 17.0, color: Colors.black),
children: <TextSpan>[
TextSpan(
text: '測試暫時有誤,待研究',
style: TextStyle(fontSize: 17.0, color: Colors.blue))
],
recognizer: TapGestureRecognizer()
..onTap = () {
print('===測試暫時有誤,待研究==');
}),
textDirection: TextDirection.ltr,
textAlign: TextAlign.center)
..layout(maxWidth: Screen.width - 40.0, minWidth: Screen.width - 40.0)
..paint(canvas, Offset(20.0, 200.0));
- 小菜認為最有意思的就是 TextSpan 中 style 的 height 屬性,在 TextSpan 中此值設定行高,是以文字基準線為最小距離;
TextPainter(
text: TextSpan(
text: 'TextPainter 小嘗試',
style: TextStyle(fontSize: 20.0, color: Colors.black54),
children: <TextSpan>[
TextSpan(
text: '\n作者:和尚(height:1.6)',
style: TextStyle(
fontSize: 14.0, color: Colors.black54, height: 1.6))
]),
textDirection: TextDirection.ltr,
textAlign: TextAlign.center)
..layout(maxWidth: Screen.width, minWidth: Screen.width)
..paint(canvas, Offset(0.0, 20.0));
TextPainter(
text: TextSpan(
text: 'TextPainter 小嘗試',
style: TextStyle(fontSize: 20.0, color: Colors.black54),
children: <TextSpan>[
TextSpan(
text: '\n作者:和尚(height:3.0)',
style: TextStyle(
fontSize: 14.0, color: Colors.black54, height: 3.0))
]),
textDirection: TextDirection.ltr,
textAlign: TextAlign.center)
..layout(maxWidth: Screen.width, minWidth: Screen.width)
..paint(canvas, Offset(0.0, 90.0));
TextPainter(
text: TextSpan(
text: 'TextPainter 小嘗試(height:0.1)',
style:
TextStyle(fontSize: 20.0, color: Colors.black54, height: 0.1),
children: <TextSpan>[
TextSpan(
text: '\nTextPainter 小嘗試(height:0.1)',
style: TextStyle(
fontSize: 20.0, color: Colors.black54, height: 0.1)),
TextSpan(
text: '\nTextPainter 小嘗試(height:0.1)',
style: TextStyle(
fontSize: 20.0, color: Colors.black54, height: 0.1))
]),
textDirection: TextDirection.ltr,
textAlign: TextAlign.center)
..layout(maxWidth: Screen.width, minWidth: Screen.width)
..paint(canvas, Offset(0.0, 220.0));
如果有不對的地方還希望多多指教!