前言通過閱讀本文,您將了解到
創(chuàng)新互聯(lián)為客戶提供專業(yè)的成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作、程序、域名、空間一條龍服務(wù),提供基于WEB的系統(tǒng)開發(fā). 服務(wù)項(xiàng)目涵蓋了網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站程序開發(fā)、WEB系統(tǒng)開發(fā)、微信二次開發(fā)、成都手機(jī)網(wǎng)站制作等網(wǎng)站方面業(yè)務(wù)。
- 文本的組成部分;
- Flutter對(duì)于文本&段落是如何繪制的;
- 明白Flutter Text 背后的邏輯;
- 在業(yè)務(wù)中碰到一些文本顯示的問題時(shí),知道從哪些地方去嘗試修改。
文字是記錄語(yǔ)言的書寫符號(hào)系統(tǒng),是形、音、義的統(tǒng)一體,是人類最重要的輔助性 交際工具。作為一個(gè)Flutter開發(fā)者,我們都知道可以通過Text()
這個(gè)文本組件將文字顯示出來。但是這其中的Flutter的字體是怎么組成的?Flutter文本是怎么構(gòu)建的?Render Tree
是怎樣繪制文本的…作為本專欄(整個(gè)專欄都在與文本打交道)的第一篇文章,讓我們從這些原理細(xì)節(jié)講起。希望能對(duì)你認(rèn)識(shí)Flutter的文本渲染有所幫助。
注:本文的目的在于讓大家了解Flutter中的基本文本知識(shí),快速的帶大家了解渲染流程,但并未很深入的分析Flutter文本渲染的原理。
字體基礎(chǔ)理論通用部分在整個(gè)網(wǎng)絡(luò)世界中,大家可以將字體理解為一個(gè)數(shù)字文件,它是一個(gè)包含特定大小、粗細(xì)和樣式的文件。它定義了每個(gè)字的形狀、大小和圖形。
例如Bariol_Regular.otf。.otf
是字體文件格式。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-SLGvJ6LK-1669544494694)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6b43356354404639bd7b54526a2970da~tplv-k3u1fbpfcp-watermark.image?)]
有了字體格式后,我們會(huì)碰到相同的字體大小卻有不同的顯示布局這個(gè)問題。因?yàn)槊恳粋€(gè)字體格式都定義了它自己的參考大小,每一個(gè)字符都是基于這個(gè)大小設(shè)計(jì)的。所以即使設(shè)置同樣的字體大小,也會(huì)有不同的布局。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-M19SgzcN-1669544494696)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/541c8653d5964a3b9358d941b2ff3661~tplv-k3u1fbpfcp-watermark.image?)]
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-flIXYZLi-1669544494697)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/06896234fa3846469fb647a380bc607b~tplv-k3u1fbpfcp-watermark.image?)]
在Flutter中文本由哪些部分組成? BaselineBaseline
(基線)上。有了這個(gè)基線后,就算是不同大小的文字也可以處于同一水平線上。Baseline
是非常重要的,因?yàn)榭梢酝ㄟ^它測(cè)量文本和元素之間的垂直距離。其他還有Middleline
、Bottomline
、Topline
。[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-VZO6UXAO-1669544494698)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2853b80fe819407b928a6fd93e29a859~tplv-k3u1fbpfcp-watermark.image?)]
TextStyle
下的wordSpacing
設(shè)置單詞與單詞之間的間距,通過letterSpacing
設(shè)置字符與字符之間的間距。[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-uyChiBG1-1669544494698)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/28a543c5d32840ee9f4a48d2ea333688~tplv-k3u1fbpfcp-watermark.image?)]
WeightWeight是指字體筆畫的粗細(xì),在Flutter中通過fontWeight
設(shè)置。
常見的有:normal
、bold
,其他還有FontWeight.w100
…等粗細(xì)值
TextStyle(
fontWeight: FontWeight.bold
),
TextSpan在Flutter中,我們經(jīng)常會(huì)使用Text()
這個(gè)組件,但是我們通過閱讀Text()
的源碼后就可以知道,它的build
方法返回的就是RichText
組件。所以它會(huì)呈現(xiàn)為TextSpan
。Span指的是字符之間的行距。
@override
Widget build(BuildContext context) {
...
Widget result = RichText(
...
text: TextSpan(
style: effectiveTextStyle,
text: data,
children: textSpan != null ?[textSpan!] : null,
),
);
...
return result;
}
Height在Flutter中,定義了一個(gè)TextStyle.height
,用于給呈現(xiàn)文本的TextSpan
一個(gè)準(zhǔn)確的行高。
TextStyle(height: 1)
但是我們需要注意,每一種字體格式都定義了自己的字體度量默認(rèn)高度,這也是為什么即使設(shè)置了相同的字體高度,也會(huì)有不同的TextSpan
的高度。
讓我們來看下這個(gè)例子:
紅色是Flutter默認(rèn)的字體,藍(lán)色是Bariol_Regular字體,綠色是Bellota-Regular字體,看看他們?cè)谙嗤?code>height下不同的框高度。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-ikr2c80A-1669544494699)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6f47d0f38c7443b8bbaad4ac3e15cc04~tplv-k3u1fbpfcp-watermark.image?)]
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-qe9aiK52-1669544494700)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fa54e9cf2b28475ea39155ede0583499~tplv-k3u1fbpfcp-watermark.image?)]
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-tbGTBpwJ-1669544494701)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1733f3d05cb2482fb868d92c7e14d8dc~tplv-k3u1fbpfcp-watermark.image?)]
這個(gè)例子也很好的驗(yàn)證了:
那么關(guān)于Flutter的字體組成我們也可以得到一個(gè)結(jié)論:使用多種字體大概率會(huì)因?yàn)榛€的不同導(dǎo)致布局不協(xié)調(diào)!
Flutter中是如何繪制文本的?通過Paragraph
,Flutter
最后繪制文本時(shí)都是通過Paragraph
完成的!
// Paragraph paragraph:文本對(duì)象
// Offset offset:文本繪制的位置
void drawParagraph(Paragraph paragraph, Offset offset)
舉個(gè)例子: 通過drawParagraph
繪制一段文字
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-C7TkjSos-1669544494702)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fc53245c2bbd4a9c86207b2cf58afbfd~tplv-k3u1fbpfcp-watermark.image?)]
import 'dart:ui' as ui;
class TextPainter extends CustomPainter {
//創(chuàng)建段落構(gòu)建器
ParagraphBuilder paragraphBuilder = ParagraphBuilder(
ParagraphStyle(fontWeight: FontWeight.bold, fontSize: 16))
..pushStyle(ui.TextStyle(color: Colors.black))
..addText('通過drawParagraph繪制的 Hello Taxze');
?
@override
void paint(Canvas canvas, Size size) {
//設(shè)置段落寬度
ParagraphConstraints paragraphConstraints =
ParagraphConstraints(width: size.width);
//計(jì)算繪制的文本位置及尺寸
Paragraph paragraph = paragraphBuilder.build()
..layout(paragraphConstraints);
//繪制
canvas.drawParagraph(paragraph, const Offset(40.0, 50.0));
}
?
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) =>false;
}
使用:
@override
Widget build(BuildContext context) {
return Scaffold(
...
body: SizedBox.expand(child: CustomPaint(painter: TextPainter())),
);
}
SizedBox.expand
包裹CustomPaint
是為了給ParagraphConstraints(width: size.width)
一個(gè)size
。你也可以用其他的組件包裹它。
關(guān)于Flutter使用CustomPaint
繪制文字的實(shí)踐較為復(fù)雜,若要講清楚繪制的主要知識(shí)點(diǎn),則需要另開一篇文章來講述。若對(duì)這個(gè)部分感興趣的朋友可以閱讀下這篇文章:Flutter學(xué)習(xí):使用CustomPaint繪制文字 — @菠蘿橙子丶
你有沒有想過,F(xiàn)lutter是如何把一段長(zhǎng)文字生成下面的這樣一個(gè)段落的呢?
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-K8zsfS9R-1669544494703)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a6aa369fa78f4762b66bfa5f64026cee~tplv-k3u1fbpfcp-watermark.image?)]
這張效果圖的代碼:
Container(
color: Colors.red,
width: 200,
height: 100,
margin: EdgeInsets.all(30),
child: Text(
"通過drawParagraph繪制的 Taxze Hello....")),
那么其中的自動(dòng)換行是怎么實(shí)現(xiàn)的呢?
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-hQ2ezYSG-1669544494704)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a4fe3f2943234c1ca5372dc902c53b57~tplv-k3u1fbpfcp-watermark.image?)]
我們知道,段落指的就是一段文本,我們要給每個(gè)字符一個(gè)合適的大小和位置。那么Flutter是如何計(jì)算這些參數(shù)的呢?
在前文說到過,F(xiàn)lutter最后繪制文本時(shí)都是通過Paragraph
完成的。Flutter就是通過Paragraph.layout
來計(jì)算這些參數(shù),而且ParagraphBuilder
給每個(gè)字符都在渲染前分配了一個(gè)偏移量。通過Paragraph
可以知道所有占位符的位置和尺寸大小。
class TextPosition {
//創(chuàng)建一個(gè)表示字符串中特定位置的對(duì)象。
const TextPosition({
required this.offset,
this.affinity = TextAffinity.downstream,
}) : assert(offset != null),
assert(affinity != null);
//舉個(gè)例子:有一個(gè)“Hello”字符,offset = 0表示光標(biāo)在字符H之前,offset = 5表示光標(biāo)在字符o之后。
final int offset;
final TextAffinity affinity;
?
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
return other is TextPosition
&& other.offset == offset
&& other.affinity == affinity;
}
?
@override
int get hashCode =>Object.hash(offset, affinity);
?
@override
String toString() {
return 'TextPosition(offset: $offset, affinity: $affinity)';
}
}
Text()背后的大哥有哪些?–文本的渲染流程
從之前講述的知識(shí)點(diǎn),Text()
組件它的build
方法返回的就是RichText
,但是Flutter
最后繪制文本時(shí)又都是通過Paragraph
完成的!那么其中的完整的一個(gè)流程是怎么樣的呢?話不多說,先上圖!
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-dDMUcy7t-1669544494704)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/dbce4fff785640e78c6c743b6ed6c409~tplv-k3u1fbpfcp-watermark.image?)]
組件層
如圖所示,每當(dāng)我們使用Text
組件時(shí),它實(shí)際上創(chuàng)建的是RichText
組件。但是RichText
和Text
不同的是,Text
將String
作為參數(shù),而RichText
將InlinSpan
作為參數(shù)(或者說是TextSpan
)。
const Text(String this.data)
//通過Text.rich構(gòu)造函數(shù)傳給RichText
const Text.rich(InlineSpan this.textSpan)
RichText(
...
text: TextSpan(
style: effectiveTextStyle,
text: data,
children: textSpan != null ?[textSpan!] : null,
),
)
//TextSpan繼承于InlineSpan
class TextSpan extends InlineSpan implements HitTestTarget, MouseTrackerAnnotation {}
因RichText
接收TextSpan
,而每一個(gè)TextSpan
都有更多的子TextSpan
,這些子TextSpan
會(huì) 繼承父TextSpan
的樣式。例如:
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-PF2spzd5-1669544494705)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/233e496530c94552aca073ef9b90a60c~tplv-k3u1fbpfcp-watermark.image?)]
RichText(
text: TextSpan(
style: Theme.of(context)
.textTheme
.bodyText1
?.copyWith(fontSize: 24),
children: [
TextSpan(
text: 'Taxze ',
),
TextSpan(text: 'blog', style: TextStyle(color: Colors.blue)),
TextSpan(
text: ' Flutter',
),
TextSpan(text: '稀土掘金', style: TextStyle(color: Colors.blue)),
]))
不過,RichText
本身是MultiChildRenderObjectWidget
的子類。它們之間有這樣的繼承關(guān)系:
class RichText extends MultiChildRenderObjectWidget {}
abstract class MultiChildRenderObjectWidget extends RenderObjectWidget {}
而MultiChildRenderObjectWidget
產(chǎn)生的MultiChildRenderObjectElement
則是這樣的關(guān)系:
class MultiChildRenderObjectElement extends RenderObjectElement {}
abstract class RenderObjectElement extends Element {}
RichText
實(shí)際上是需要一個(gè)InlineSpan
,而InlineSpan
可以是TextSpan
或者是WidgetSpan
。對(duì)WidgetSpan有興趣的朋友,可以參考官方的文檔WidgetSpan。
到這里為止,我們可以將RichText
(包括RichText)之前的所有劃分為組件層,那么我們現(xiàn)在就要進(jìn)入渲染層了。
渲染層
我們已經(jīng)知道了RichText
會(huì)創(chuàng)建一個(gè)渲染對(duì)象—RenderParagraph
,那么RenderParagraph
是干什么的呢?
RichText
是MultiChildRenderObjectWidget
的子類,它會(huì)把MultiChildRenderObjectElement
往下傳遞,但是此時(shí)MultiChildRenderObjectElement
沒有渲染,它還沒有什么作用。這個(gè)時(shí)候RichText
會(huì)給它一個(gè)RenderParagraph
,RenderParagraph
會(huì)收到RenderPadding
的指令,這個(gè)時(shí)候MultiChildRenderObjectElement
就準(zhǔn)備好了一切,就可以開始工作了。
這樣解釋可能有點(diǎn)抽象,那么我們來看下這個(gè)例子:
body: Container(
alignment: Alignment.center,
child: Text("Taxze Hello"), ,
)
很簡(jiǎn)單的一個(gè)小例子,它的結(jié)構(gòu)也很清晰:
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-yhsYCYUC-1669544494705)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ef477b4af1b849f3a3cf5aa3f68c8466~tplv-k3u1fbpfcp-watermark.image?)]
當(dāng)Flutter把三棵樹都構(gòu)建完后:
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-zinkstt2-1669544494706)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/58bc0cb3d88a4dc1b08305026eef423a~tplv-k3u1fbpfcp-watermark.image?)]
那么當(dāng)我們改變文本時(shí),又會(huì)發(fā)生什么呢?
最先改變的當(dāng)然是組件層:
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-94R0jawX-1669544494706)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/313481d32f85479c8eb3dd8ffe47b545~tplv-k3u1fbpfcp-watermark.image?)]
我們會(huì)有一個(gè) “新” 的組件樹。不過你真的認(rèn)為都是新的嗎?Flutter會(huì)充分利用現(xiàn)有的元素,讓我們來看下這個(gè)名為canUpdate
的方法吧。
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
通過這個(gè)方法,F(xiàn)lutter可以檢查一個(gè)老的組件的Type
和key
,并把它和新的組件進(jìn)行比較。如果它們都相同的話,就不需要更新。
所以就算更新后,Container
更新之后它還是存在的,而且我們沒有給它一個(gè)Key,所以OldContainer
和NewContainer
是完全相同的。Align、Text、以及RichText它們的Type和Key都沒有變化,重新構(gòu)建它們沒有什么意義,所以它們都不會(huì)有更新。
到這里,我猜你肯定會(huì)問,都沒有更新,那么文本是如何改變的呢?
那么我們就要講到組件中的屬性了。組件除了具有Type和Key之外,還有屬性。屬性的改變會(huì)使RenderParagraph
顯示新的文本。
不過關(guān)于文本的更改渲染到現(xiàn)在我們都是在紙上談兵,那么我們現(xiàn)在就來用一個(gè)簡(jiǎn)單的例子去驗(yàn)證之前的結(jié)論。
bool _isFirst = true;
?
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.swap_horiz),
onPressed: () {
setState(() {
_isFirst = !_isFirst;
});
},
),
body: _isFirst ? first() : second());
}
}
?
Widget first() =>Container(
alignment: Alignment.center,
child: const Text("Taxze First"),
);
?
Widget second() =>Container(
alignment: Alignment.center,
child: const Text("Taxze Second"),
);
非常簡(jiǎn)單的一個(gè)例子,點(diǎn)擊按鈕更改顯示文字。當(dāng)我們點(diǎn)下按鈕時(shí),文本改變后,所有的組件都會(huì)重用,F(xiàn)lutter只會(huì)重建RenderPadding
。
繪制層
在渲染層中,我們最后發(fā)生文本變化都在RenderParagraph
上,不過RenderParagraph
并不會(huì)直接的繪制文本,而是會(huì)創(chuàng)建一TextPainter
來管理繪制的工作。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-JgUMZp4Z-1669544494706)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f358e123221c48ef8891ea7a5ef49620~tplv-k3u1fbpfcp-watermark.image?)]
不過,TextPainter
做的事和它的名字完全不一樣,你以為就是它來繪制文本的嗎?No~
實(shí)際上,它只是負(fù)責(zé)管理繪制的事,但它自己不會(huì)去繪制(當(dāng)老板)。
基礎(chǔ)層
到現(xiàn)在為止,你會(huì)發(fā)現(xiàn),講了那么多,但是還是沒有那個(gè)ta去真正的繪制文本,就好像之前的所有的組件都在當(dāng)中間商,把活外包了出去。
到了Flutter的最底層,你會(huì)發(fā)現(xiàn)有一個(gè)ParagraphBuilder
和Paragraph
,在前面關(guān)于Flutter如何繪制文本中,我們也提到了Flutter
最后繪制文本時(shí)都是通過Paragraph
完成的,而TextPainter
是負(fù)責(zé)創(chuàng)建ParagraphBuilder
的,但是當(dāng)你翻看Paragraph
類的源碼時(shí),你會(huì)發(fā)現(xiàn),大部分的函數(shù)都是空函數(shù),原來這哥們也沒干活?。?/p>
@pragma('vm:entry-point')
class Paragraph extends NativeFieldWrapperClass1 {
@pragma('vm:entry-point')
Paragraph._();
?
bool _needsLayout = true;
?
double get width native 'Paragraph_width';
?
double get height native 'Paragraph_height';
?
double get longestLine native 'Paragraph_longestLine';
?
double get minIntrinsicWidth native 'Paragraph_minIntrinsicWidth';
?
double get maxIntrinsicWidth native 'Paragraph_maxIntrinsicWidth';
?
double get alphabeticBaseline native 'Paragraph_alphabeticBaseline';
...
}
引擎層
當(dāng)Paragraph
和ParagraphBuilder
這兩個(gè)類都將繪制的工作交給了Flutter Engine
后,我們也要將視線放到SkParagraph
上了,在以前Flutter Engine
處理文本繪制的庫(kù)是LibText
。后面切換成了SkParagraph
,但是也實(shí)現(xiàn)了和Libtext
相同的API。對(duì)于Flutter引擎在這篇文章中只做一個(gè)簡(jiǎn)單的說明,若對(duì)引擎感興趣的朋友可以自己編譯FlutterEngine進(jìn)行學(xué)習(xí),或者在線閱讀。
–更詳細(xì)更深入的Flutter文本渲染原理有興趣的朋友可以閱讀這篇文章
解決Flutter文本基線不對(duì)齊的問題經(jīng)常在各大Flutter交流群中看到有哥們問這樣的問題:Row中,兩個(gè)文本沒有對(duì)齊,這怎么處理呀?
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-jLL0YCuo-1669544494707)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/327583bd15264122a5b1e9c4bc2c0aca~tplv-k3u1fbpfcp-watermark.image?)]
展示圖代碼:
Center(
child: Row(
children: [
ColoredBox(
color: Colors.amber,
child: Text.rich(TextSpan(children: [
TextSpan(text: "¥999", style: TextStyle(fontSize: 28)),
TextSpan(text: ".9", style: TextStyle(fontSize: 14)),
])),
),
ColoredBox(
color: Colors.red,
child: Text.rich(TextSpan(children: [
TextSpan(text: "123", style: TextStyle(fontSize: 12)),
])),
),
],
),
)
其實(shí)處理這個(gè)問題很簡(jiǎn)單,只需要給Row加上:
textBaseline: TextBaseline.alphabetic,
crossAxisAlignment: CrossAxisAlignment.baseline,
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-JFwhozn4-1669544494707)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/add602d2e218444e8ccf7c3f157b108b~tplv-k3u1fbpfcp-watermark.image?)]
關(guān)于更多有關(guān)文本的布局問題大家可以查看官方這篇文檔。
尾述在這篇文章中,我們知道了文本是由什么組成的,F(xiàn)lutter是怎樣將文本顯示到屏幕上的。但是這也只是Flutter關(guān)于文本的一小部分,關(guān)于文本的編輯…等內(nèi)容將會(huì)在后續(xù)的文章中繼續(xù)探索。希望這篇文章能對(duì)你有所幫助,有問題歡迎在評(píng)論區(qū)留言討論~
參考&推薦閱讀Flutter Text Rendering — @Jonathan Sande
書后拓展:Flutter 中一行文字到屏幕上,渲染全過程! — @MeandNi
Flutter 小技巧之玩轉(zhuǎn)字體渲染和問題修復(fù) — @戀貓de小郭
Flutter學(xué)習(xí):使用CustomPaint繪制文字 — @菠蘿橙子丶
關(guān)于我Hello,我是Taxze,如果您覺得文章對(duì)您有價(jià)值,希望您能給我的文章點(diǎn)個(gè)??,有問題需要聯(lián)系我的話:我在這里
如果您覺得文章還差了那么點(diǎn)東西,也請(qǐng)通過關(guān)注督促我寫出更好的文章——萬(wàn)一哪天我進(jìn)步了呢?😝
— 文字是人類用符號(hào)記錄表達(dá)信息以傳之久遠(yuǎn)的方式和工具。
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧
當(dāng)前名稱:Flutter中那些你需要知道的文本知識(shí)!-創(chuàng)新互聯(lián)
文章來源:http://chinadenli.net/article40/djjgho.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站改版、關(guān)鍵詞優(yōu)化、小程序開發(fā)、網(wǎng)站建設(shè)、網(wǎng)站策劃、網(wǎng)站維護(hù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容