一、概述
在 Flutter 开发中,开发者几乎都在与 Widget 打交道。Flutter 本身为开发者提供了很多内置的 Widget,开发者也可以自定义自己的 Widget,下面就开始对 Flutter 内置的 Widget 做一些基础的说明。以下的介绍中尽量使所有的示例代码直接可以运行,所以我们将上一篇文章中的默认代码( main.dart
中的)全部删除,然后重新添加 main
函数如下:
import 'package:flutter/material.dart';
void main() {
runApp(app); //将在 Widget 说明的过程中,替换app
}
二、Text Widget
Flutter 中提供了两种显示文本的 Widget ,Text
和 RichText
。实际上,Text
内部就是使用 RichText
实现的,Text
的 build
方法返回的是 RichText
的实例。Text
与 RichText
的区别在于 Text
使用最接近封闭 DefaultTextStyle
对象的样式,RichText
需要显式样式。这里先讲解 Text
,RichText
将在稍后讲解。
文本 Widget 用于显示单一样式的文本字符串,可以显示单行文本,也可以显示多行文本。
其继承关系如下:
Text < StatelessWidget < Widget < DiagnosticableTree < Diagnosticable < Object
可见其为无状态 Widget,它有两个构造方法,第一个是 Text()
,第二个是 Text.rich()
。
1. Text() 构造方法
const Text(
//String类型必传参数,为要显示的文本字符串
this.data, {
//以下为可选命名参数
//
Key key,
//TextStyle类型可选参数,用于设置文本的样式
this.style,
//StrutStyle类型可选参数,用于设置文本支撑样式
this.strutStyle,
//TextAlign类型可选参数,用于设置文本水平方向如何对齐
this.textAlign,
//TextDirection类型参数,用于设置文本的显示方向(从左到右或从右到左)
this.textDirection,
//Locale类型参数,用于设置多语言,可以根据不同的区域设置以不同的方式呈现相同的 Unicode 字符。
this.locale,
//bool类型参数,用于设置文本是否自动换行,如果为 `true` ,自动换行显示,`false` 则不换行
//在一行显示,超出屏幕部分不显示。默认为 `true`
this.softWrap,
//TextOverflow类型参数,用于设置文本溢出时如何显示
this.overflow,
//double类型参数,用于设置文本的缩放倍数
this.textScaleFactor,
//int类型参数,用于设置最大显示行数,如果超出限制则会根据 overflow 设置的溢出样式显示
this.maxLines,
//String类型参数,于设置显示文本的替代语义标签
this.semanticsLabel,
//TextWidthBasis类型参数 ,用于设置如何测量渲染文本的宽度
this.textWidthBasis,
})
Text
是一个的组件, textDirection
属性,它是一个命名可选参数,但是却是需要必须提供的参数,如果在应用的 Widget 树环境中其他的父级 Widget 有可以确定环境方向性,Text
便会默认使用环境中的方向,如果没有方向性的设置,则要在 Text()
构造函数中显式设置,否则会抛出异常,如下:
import 'package:flutter/material.dart';
void main() {
Text cText = Text("Hello world!");
runApp(cText);
}
因为创建的 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
,意思是从左到右 或 从右到左。
上述代码是为了让大家看的明白如果进行创建对象,可以对其进行代码简化,如下:
import 'package:flutter/material.dart';
void main() {
runApp(Text(
"Hello world!",
textDirection: TextDirection.ltr,
)
);
}
执行结果如下图:
因为没有导航等布局组件,所以直接加载的 Widget 从左上角开始布局。为了使创建的程序看起来更像一个应用,对代码做如下更改:
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
的无状态组件,前面文章对 StatelessWidget
做过说明。重写了抽象方法 build
,并返回了一个 MaterialApp
Widget ,在这里,大家要知道 MaterialApp
是在创建 Material 设计风格的移动应用程序中,作为 Widget 树的根使用。home
属性用来设置应用启动后要显示的首页 Widget 。Scaffold
为脚手架 Widget ,在这里用来设置应用程序的顶部导航和主体显示内容。其中,body
接收一个 Widget 参数,我们通过创建返回一个 Widget 的函数进行了设置。对于 MaterialApp
和 Scaffold
都还有各自的其他很多功能,后续文章会做详细说明。在这里只是使应用看起来更美观而使用。我们把 Text
Widget 放在了函数 studyWidget()
中。在更改后的代码中,我们在创建 Text
的时候并没有指定方向,因为使用了 MaterialApp
,它默认有方向性的设置为 LTR ,从上面抛出的异常提示中也有说明。
执行效果如下:
2. Text.rich() 构造方法
const Text.rich(
//InlineSpan类型必传参数,应使用TextSpan
this.textSpan, {
//...省略可选参数,可选参数与上述Text()相同
})
InlineSpan
是一个抽象类,其有三个直接或间接子类,分别为:TextSpan
、PlaceholderSpan
、WidgetSpan
,其中 WidgetSpan
继承自 PlaceholderSpan
。
TextSpan
表示一个不变的文本范围,构造函数如下:
const TextSpan({
//String类型可选参数,为要显示的文本
this.text,
//List类型可选参数,子Widget
this.children,
//TextStyle类型可选参数,文本样式
TextStyle style,
//GestureRecognizer类型可选参数,手势识别器,用于接收手势事件
this.recognizer,
//String类型可选参数,用于定义替代的语义标签
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],
),
);
}
执行效果:
PlaceholderSpan
PlaceholderSpan
也是一个抽象类,不能直接被实例化,必须对此类进行扩展才能使用。作用是作为嵌入在文本中不可变的占位符。可使用其子类 WidgetSpan
。
WidgetSpan
WidgetSpan
用于嵌入在文本中不可变的 Widget 。 构造函数如下:
const WidgetSpan({
//Widget类型必传参数,为内嵌在文本的中的Widget
@required this.child,
//PlaceholderAlignment类型参数,用于设置内嵌的Widget与文本数据如何对齐,默认基线对齐
ui.PlaceholderAlignment alignment = ui.PlaceholderAlignment.bottom,
//TextBaseline类型参数,用于在设置alignment参数时的基线
TextBaseline baseline,
//TextStyle类型参数,设置文本样式
TextStyle style,
})
使用如下:
Widget studyWidget() {
return Text.rich(
TextSpan(
children: [
TextSpan(text: "开始",),
WidgetSpan(
child: Text("HHHHHH"),
),
TextSpan(text: "结束",),
],
),
);
}
child
属性可以设置任何占位 Widget 。
3. TextStyle
TextStyle
是文本中的属性,用于设置文本样式,其构造函数如下:
const TextStyle({
//是否将空值替换为父级Widget文本样式中的值,也就是子Widget在没有定义样式
//的情况下是否继承父级Widget中的文本样式
this.inherit = true,
//Color类型对象,用于设置文本字体颜色
this.color,
//Color类型对象,用于设置文本背景色
this.backgroundColor,
//double类型可选参数,字体大小
this.fontSize,
//FontWeight类型参数,粗体
this.fontWeight,
//FontStyle类型参数,设置字体样式,如斜体等
this.fontStyle,
//double类型可选参数,设置字母间距(空格)
this.letterSpacing,
//double类型可选参数,设置单词之间的间距
this.wordSpacing,
//TextBaseline类型可选参数,用于设置不同范围文本间的对齐基线
this.textBaseline,
//double类型可选参数,设置文本跨度的高度。当height为null或省略时,
//行高将直接由字体的度量标准确定,这可能与fontSize不同。当height
//为非空时,文本范围的行高将是fontSize的倍数,并且正好是fontSize * height逻辑像素高
this.height,
//Locale类型可选参数,多语言
this.locale,
//Paint类型可选参数,绘制文本的前景样式,比如描边文字等
this.foreground,
//Paint类型可选参数,绘制文本的背景样式,可以设置填充,描边,画笔宽度等
this.background,
//List类型可选参数,用于在文字下方绘制阴影
this.shadows,
//List类型可选参数,用于设置影响显示字体样式的列表
this.fontFeatures,
//TextDecoration类型可选参数,用于设置文字附近的装饰,例如下划线
this.decoration,
//Color类型可选参数,用于设置文字装饰的颜色
this.decorationColor,
//TextDecorationStyle类型参数,用于设置文字装饰的样式
this.decorationStyle,
//double类型可选参数,设置文字装饰的笔触的粗细诚意字体定义的粗细
this.decorationThickness,
//String类型可选参数,文本风格调式,调试版本可用
this.debugLabel,
//String类型可选参数,用于设置绘制文本时使用的字体名称
String fontFamily,
//list类型可选参数,当在较高优先级的字体系列中找不到字形时,字体系列的有序列表将重新出现
List fontFamilyFallback,
//String类型可选参数,要使用包中定义的字体系列,必须提供package参数
String package,
})
下面对上面的样式做个例子如下:
Widget studyWidget() {
return Column(
children: [
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,
),
),
],
);
}
执行效果如下:
上面用到了纵向布局的 Column
,它的 children
属性可以接收多个子 Widget ,并且纵向排列,这里只是用一下,后续会详细介绍。
三、RichText
RichText
是 Flutter 中的富文本,没有提供必传参数,但是在可选命名参数中,InlineSpan
类型的 text
使用 @required
关键字修饰了,@required
修饰的属性也是必传属性,否则会报警告。其他可选参数在 Text
中也存在,功能相同,部分属性提供了默认值。
RichText({
Key key,
//InlineSpan类型必传参数,使用 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
中有作说明,这里不再赘述,可以参考以上同名参数。使用方式也与 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
是容器组件,后续会讲解。