一、概述
在 Flutter 開發(fā)中,開發(fā)者幾乎都在與 Widget 打交道。Flutter 本身為開發(fā)者提供了很多內(nèi)置的 Widget,開發(fā)者也可以自定義自己的 Widget,下面就開始對 Flutter 內(nèi)置的 Widget 做一些基礎的說明。以下的介紹中盡量使所有的示例代碼直接可以運行,所以我們將上一篇文章中的默認代碼( main.dart 中的)全部刪除,然后重新添加 main 函數(shù)如下:
import 'package:flutter/material.dart';
void main() {
runApp(app); //將在 Widget 說明的過程中,替換app
}
二、Text Widget
Flutter 中提供了兩種顯示文本的 Widget ,Text 和 RichText 。實際上,Text 內(nèi)部就是使用 RichText 實現(xiàn)的,Text 的 build 方法返回的是 RichText 的實例。Text 與 RichText 的區(qū)別在于 Text 使用最接近封閉 DefaultTextStyle 對象的樣式,RichText 需要顯式樣式。這里先講解 Text ,RichText 將在稍后講解。
文本 Widget 用于顯示單一樣式的文本字符串,可以顯示單行文本,也可以顯示多行文本。
其繼承關系如下:
Text < StatelessWidget < Widget < DiagnosticableTree < Diagnosticable < Object
可見其為無狀態(tài) Widget,它有兩個構造方法,第一個是 Text() ,第二個是 Text.rich() 。
1. Text() 構造方法
const Text(
//String類型必傳參數(shù),為要顯示的文本字符串
this.data, {
//以下為可選命名參數(shù)
//
Key key,
//TextStyle類型可選參數(shù),用于設置文本的樣式
this.style,
//StrutStyle類型可選參數(shù),用于設置文本支撐樣式
this.strutStyle,
//TextAlign類型可選參數(shù),用于設置文本水平方向如何對齊
this.textAlign,
//TextDirection類型參數(shù),用于設置文本的顯示方向(從左到右或從右到左)
this.textDirection,
//Locale類型參數(shù),用于設置多語言,可以根據(jù)不同的區(qū)域設置以不同的方式呈現(xiàn)相同的 Unicode 字符。
this.locale,
//bool類型參數(shù),用于設置文本是否自動換行,如果為 `true` ,自動換行顯示,`false` 則不換行
//在一行顯示,超出屏幕部分不顯示。默認為 `true`
this.softWrap,
//TextOverflow類型參數(shù),用于設置文本溢出時如何顯示
this.overflow,
//double類型參數(shù),用于設置文本的縮放倍數(shù)
this.textScaleFactor,
//int類型參數(shù),用于設置最大顯示行數(shù),如果超出限制則會根據(jù) overflow 設置的溢出樣式顯示
this.maxLines,
//String類型參數(shù),于設置顯示文本的替代語義標簽
this.semanticsLabel,
//TextWidthBasis類型參數(shù) ,用于設置如何測量渲染文本的寬度
this.textWidthBasis,
})
Text 是一個的組件, textDirection 屬性,它是一個命名可選參數(shù),但是卻是需要必須提供的參數(shù),如果在應用的 Widget 樹環(huán)境中其他的父級 Widget 有可以確定環(huán)境方向性,Text 便會默認使用環(huán)境中的方向,如果沒有方向性的設置,則要在 Text() 構造函數(shù)中顯式設置,否則會拋出異常,如下:
import 'package:flutter/material.dart';
void main() {
Text cText = Text("Hello world!");
runApp(cText);
}
因為創(chuàng)建的 Text 實例為一個單獨的 Widget ,并且直接使用 runApp() 運行,所以此 Text 便是此程序 Widget 樹的根,并沒有父級 Widget,也就無法使用其他 Widget 的方向性,且沒有提供方向屬性, 會拋出如下異常:
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following assertion was thrown building Text("Hello world!"):
No Directionality widget found.
RichText widgets require a Directionality widget ancestor.
The specific widget that could not find a Directionality ancestor was: RichText
softWrap: wrapping at box width
maxLines: unlimited
text: "Hello world!"
dirty
The ownership chain for the affected widget is: "RichText ← Text ← [root]"
Typically, the Directionality widget is introduced by the MaterialApp or WidgetsApp widget at the top of your application widget tree. It determines the ambient reading direction and is used, for example, to determine how to lay out text, how to interpret "start" and "end" values, and to resolve EdgeInsetsDirectional, AlignmentDirectional, and other *Directional objects.
正確的使用方法如下:
import 'package:flutter/material.dart';
void main() {
Text cText = Text(
"Hello world!",
textDirection: TextDirection.ltr, //設置方向 從左往右
);
runApp(cText);
}
TextDirection 為枚舉類型,值為 ltr 或 rtl ,意思是從左到右 或 從右到左。
上述代碼是為了讓大家看的明白如果進行創(chuàng)建對象,可以對其進行代碼簡化,如下:
import 'package:flutter/material.dart';
void main() {
runApp(Text(
"Hello world!",
textDirection: TextDirection.ltr,
)
);
}
執(zhí)行結果如下圖:

因為沒有導航等布局組件,所以直接加載的 Widget 從左上角開始布局。為了使創(chuàng)建的程序看起來更像一個應用,對代碼做如下更改:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Widget基礎"),
),
body: studyWidget(),
),
);
}
}
Widget studyWidget() {
return Text(
"Hello world!",
);
}
這里自定義了一個 Widget 為 MyApp,它是繼承自 StatelessWidget 的無狀態(tài)組件,前面文章對 StatelessWidget 做過說明。重寫了抽象方法 build ,并返回了一個 MaterialApp Widget ,在這里,大家要知道 MaterialApp 是在創(chuàng)建 Material 設計風格的移動應用程序中,作為 Widget 樹的根使用。home 屬性用來設置應用啟動后要顯示的首頁 Widget 。Scaffold 為腳手架 Widget ,在這里用來設置應用程序的頂部導航和主體顯示內(nèi)容。其中,body 接收一個 Widget 參數(shù),我們通過創(chuàng)建返回一個 Widget 的函數(shù)進行了設置。對于 MaterialApp 和 Scaffold 都還有各自的其他很多功能,后續(xù)文章會做詳細說明。在這里只是使應用看起來更美觀而使用。我們把 Text Widget 放在了函數(shù) studyWidget() 中。在更改后的代碼中,我們在創(chuàng)建 Text 的時候并沒有指定方向,因為使用了 MaterialApp ,它默認有方向性的設置為 LTR ,從上面拋出的異常提示中也有說明。
執(zhí)行效果如下:

2. Text.rich() 構造方法
const Text.rich(
//InlineSpan類型必傳參數(shù),應使用TextSpan
this.textSpan, {
//...省略可選參數(shù),可選參數(shù)與上述Text()相同
})
InlineSpan 是一個抽象類,其有三個直接或間接子類,分別為:TextSpan 、PlaceholderSpan 、WidgetSpan ,其中 WidgetSpan 繼承自 PlaceholderSpan 。
TextSpan
表示一個不變的文本范圍,構造函數(shù)如下:
const TextSpan({
//String類型可選參數(shù),為要顯示的文本
this.text,
//List<InlineSpan>類型可選參數(shù),子Widget
this.children,
//TextStyle類型可選參數(shù),文本樣式
TextStyle style,
//GestureRecognizer類型可選參數(shù),手勢識別器,用于接收手勢事件
this.recognizer,
//String類型可選參數(shù),用于定義替代的語義標簽
this.semanticsLabel,
})
使用方式如下:
Widget studyWidget() {
return Text.rich(
TextSpan(
text: "Hooray! ",
style: TextStyle(color: Colors.black, fontSize: 24),
),
);
}
也可以定義多個范圍的文本,使用 children 屬性設置,如下:
Widget studyWidget() {
TextSpan cSpan1 = TextSpan(
text: "Hooray! ",
style: TextStyle(color: Colors.black, fontSize: 24),
);
TextSpan cSpan2 = TextSpan(
text: "It's snowing! It's time to make a snowman.James runs out.",
style: TextStyle(color: Colors.green, fontSize: 18),
);
TextSpan cSpan3 = TextSpan(
text: "He adds a scarf and a hat.",
style: TextStyle(color: Colors.blue, fontSize: 30),
);
return Text.rich(
TextSpan(
children: [cSpan1, cSpan2, cSpan3],
),
);
}
執(zhí)行效果:

PlaceholderSpan
PlaceholderSpan 也是一個抽象類,不能直接被實例化,必須對此類進行擴展才能使用。作用是作為嵌入在文本中不可變的占位符??墒褂闷渥宇?WidgetSpan 。
WidgetSpan
WidgetSpan 用于嵌入在文本中不可變的 Widget 。 構造函數(shù)如下:
const WidgetSpan({
//Widget類型必傳參數(shù),為內(nèi)嵌在文本的中的Widget
@required this.child,
//PlaceholderAlignment類型參數(shù),用于設置內(nèi)嵌的Widget與文本數(shù)據(jù)如何對齊,默認基線對齊
ui.PlaceholderAlignment alignment = ui.PlaceholderAlignment.bottom,
//TextBaseline類型參數(shù),用于在設置alignment參數(shù)時的基線
TextBaseline baseline,
//TextStyle類型參數(shù),設置文本樣式
TextStyle style,
})
使用如下:
Widget studyWidget() {
return Text.rich(
TextSpan(
children: <InlineSpan>[
TextSpan(text: "開始",),
WidgetSpan(
child: Text("HHHHHH"),
),
TextSpan(text: "結束",),
],
),
);
}
child 屬性可以設置任何占位 Widget 。
3. TextStyle
TextStyle 是文本中的屬性,用于設置文本樣式,其構造函數(shù)如下:
const TextStyle({
//是否將空值替換為父級Widget文本樣式中的值,也就是子Widget在沒有定義樣式
//的情況下是否繼承父級Widget中的文本樣式
this.inherit = true,
//Color類型對象,用于設置文本字體顏色
this.color,
//Color類型對象,用于設置文本背景色
this.backgroundColor,
//double類型可選參數(shù),字體大小
this.fontSize,
//FontWeight類型參數(shù),粗體
this.fontWeight,
//FontStyle類型參數(shù),設置字體樣式,如斜體等
this.fontStyle,
//double類型可選參數(shù),設置字母間距(空格)
this.letterSpacing,
//double類型可選參數(shù),設置單詞之間的間距
this.wordSpacing,
//TextBaseline類型可選參數(shù),用于設置不同范圍文本間的對齊基線
this.textBaseline,
//double類型可選參數(shù),設置文本跨度的高度。當height為null或省略時,
//行高將直接由字體的度量標準確定,這可能與fontSize不同。當height
//為非空時,文本范圍的行高將是fontSize的倍數(shù),并且正好是fontSize * height邏輯像素高
this.height,
//Locale類型可選參數(shù),多語言
this.locale,
//Paint類型可選參數(shù),繪制文本的前景樣式,比如描邊文字等
this.foreground,
//Paint類型可選參數(shù),繪制文本的背景樣式,可以設置填充,描邊,畫筆寬度等
this.background,
//List<ui.Shadow>類型可選參數(shù),用于在文字下方繪制陰影
this.shadows,
//List<FontFeature>類型可選參數(shù),用于設置影響顯示字體樣式的列表
this.fontFeatures,
//TextDecoration類型可選參數(shù),用于設置文字附近的裝飾,例如下劃線
this.decoration,
//Color類型可選參數(shù),用于設置文字裝飾的顏色
this.decorationColor,
//TextDecorationStyle類型參數(shù),用于設置文字裝飾的樣式
this.decorationStyle,
//double類型可選參數(shù),設置文字裝飾的筆觸的粗細誠意字體定義的粗細
this.decorationThickness,
//String類型可選參數(shù),文本風格調(diào)式,調(diào)試版本可用
this.debugLabel,
//String類型可選參數(shù),用于設置繪制文本時使用的字體名稱
String fontFamily,
//list<String>類型可選參數(shù),當在較高優(yōu)先級的字體系列中找不到字形時,字體系列的有序列表將重新出現(xiàn)
List<String> fontFamilyFallback,
//String類型可選參數(shù),要使用包中定義的字體系列,必須提供package參數(shù)
String package,
})
下面對上面的樣式做個例子如下:
Widget studyWidget() {
return Column(
children: <Widget>[
Text(
"Hooray! It's snowing! It's time to make a snowman.James runs out. He makes a big pile of snow. He puts a big snowball on top. He adds a scarf and a hat",
style: TextStyle(
fontSize: 16,
),
maxLines: 2, //最多顯示2行文本
overflow: TextOverflow.ellipsis, //超出部分以...表示
textScaleFactor: 1.2, //字體放大1.2倍
),
Text( //設置字體顏色和背景顏色
"Text Color And backgroundColor",
style: TextStyle(
color: Colors.yellow,
backgroundColor: Color(0xFF00FF00),
),
),
Text( //設置字體大小 粗體 斜體
"Font Size And FontWeight",
style: TextStyle(
fontSize: 30.0,
fontWeight: FontWeight.bold,
fontStyle: FontStyle.italic,
),
),
Text( //設置字母和單詞直接的間距
"hello world! 這是一句中文!",
style: TextStyle(
letterSpacing: 1.0,
wordSpacing: 10.0
),
),
Text(
"文本的height跨越高度",
style: TextStyle(
height: 2,
fontSize: 30,
backgroundColor: Colors.red,
),
),
Text(
"文本前景",
style: TextStyle(
foreground: Paint()
..color = Colors.blue
..strokeWidth = 1.0
..style = PaintingStyle.stroke
,
fontSize: 40,
),
),
Text(
"文本背景",
style: TextStyle(
background: Paint()
..color = Colors.blue
..strokeWidth = 1.0
..style = PaintingStyle.stroke
,
fontSize: 40,
),
),
Text(
"文本的陰影",
style: TextStyle(
fontSize: 30,
shadows: [
Shadow(
color: Colors.cyan,
offset: Offset(2, 2),
blurRadius: 2,
),
],
),
),
Text(
"文本裝飾,這是文本裝飾樣式",
style: TextStyle(
fontSize: 30,
decoration: TextDecoration.underline,
decorationColor: Colors.purpleAccent,
decorationStyle: TextDecorationStyle.dashed,
),
),
],
);
}
執(zhí)行效果如下:

上面用到了縱向布局的 Column ,它的 children 屬性可以接收多個子 Widget ,并且縱向排列,這里只是用一下,后續(xù)會詳細介紹。
三、RichText
RichText 是 Flutter 中的富文本,沒有提供必傳參數(shù),但是在可選命名參數(shù)中,InlineSpan 類型的 text 使用 @required 關鍵字修飾了,@required 修飾的屬性也是必傳屬性,否則會報警告。其他可選參數(shù)在 Text 中也存在,功能相同,部分屬性提供了默認值。
RichText({
Key key,
//InlineSpan類型必傳參數(shù),使用 TextSpan 進行設置
@required this.text,
this.textAlign = TextAlign.start,
this.textDirection,
this.softWrap = true,
this.overflow = TextOverflow.clip,
this.textScaleFactor = 1.0,
this.maxLines,
this.locale,
this.strutStyle,
this.textWidthBasis = TextWidthBasis.parent,
})
上述屬性在 Text 中有作說明,這里不再贅述,可以參考以上同名參數(shù)。使用方式也與 Text.rich() 相同。
四、其他說明
1. TextAlgin
在對文本對齊進行設置中,需要使用 TextAlign ,它是一個枚舉類型,如下:
enum TextAlign {
//在容器的左側對齊文本,與ltr或rtl無關
left,
//在容器的右側對齊文本,與ltr或rtl無關
right,
//在容器的中心對齊文本,與ltr或rtl無關
center,
//以自動換行的當前行文本自動拉伸以填充容器寬度,對于沒有換行的文本,進行邊緣對齊。
justify,
//在容器的尾部對齊。對于ltr的文本,在容器的左側對齊,對于rtl的文本在容器右側對齊
start,
//在容器的前端對齊。對于ltr的文本,在容器的右側對齊,對于rtl的文本在容器的左側對齊
end,
}
設置方式如下代碼:
Widget studyWidget(BuildContext context) {
String textString = "Hooray! It's snowing! It's time to make a snowman.James runs out. He makes a big pile of snow. He puts a big snowball on top. He adds a scarf and a hat";
return Container(
child: Text(
textString,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: 24,
),
textDirection: TextDirection.ltr,
),
width: 300,
height: 200,
color: Colors.red,
);
}
Container 是容器組件,后續(xù)會講解。