在构建 Flutter 应用时,大部分时间你都在处理小部件。本章提供了关于 Flutter 中窗口小部件的基本背景信息。它还介绍了几个显示文本、图像、图标、按钮和占位符的基本小部件。
你想知道如何在 Flutter 中使用组件。
在 Flutter 中,小部件无处不在。
如果你已经参与了用户界面的开发,你应该熟悉像部件或组件这样的概念。这些概念代表了创建用户界面的可重用构件。一个好的用户界面库应该有大量的高质量且易于使用的组件。按钮、图标、图像、菜单、对话框和表单输入都是组件的例子。组件可大可小。复杂组件通常由小组件组成。您可以通过遵循组件模型来创建自己的组件。您还可以选择将您的组件共享给社区。良好的组件生态系统是用户界面库成功的关键因素。
Flutter 使用小部件来描述用户界面中可重用的构件。与其他库相比,Flutter 中的小部件是一个更广泛的概念。不仅按钮和表单输入等常见组件是窗口小部件,布局约束在 Flutter 中也表示为窗口小部件。例如,如果您想将一个小部件放在一个框的中央,您只需将这个小部件包装成一个Center
小部件。小部件也用于检索上下文数据。例如,DefaultTextStyle
小部件得到的TextStyle
应用于未样式化的Text
小部件。
Flutter 中的小部件是用户界面一部分的不可变描述。小部件类的所有字段都是最终的,并在构造函数中设置。小部件构造函数只有命名参数。一个部件可以有一个或多个部件作为子部件。Flutter 应用的小部件创建了一个树状层次结构。Flutter 应用入口点文件的main()
方法使用runApp()
方法启动应用。runApp()
的唯一参数是一个Widget
对象。这个Widget
对象是应用小部件树的根。小部件只是静态配置,描述如何配置层次结构中的子树。为了实际运行这个应用,我们需要一种方法来管理小部件的实例化。
Flutter 使用Element
来表示树中特定位置的Widget
的实例化。一个Widget
可以被实例化零次或多次。将 T3 转变为 T4 的过程叫做膨胀。Widget
类有一个createElement()
方法来将小部件膨胀为Element
的具体实例。Flutter 框架负责管理元素的生命周期。与元素相关联的窗口小部件可能会随着时间而改变。框架更新元素以使用新的配置。
当运行应用时,Flutter framework 负责渲染元素以创建渲染树,因此最终用户可以实际看到用户界面。渲染树由根为RenderView
的RenderObject
组成。如果你使用的是 Android Studio,你可以在 Flutter Inspector 视图中看到窗口小部件树和渲染树。选择查看➤工具窗口➤Flutter 检查器打开 Flutter 检查器视图。图 4-1 显示了 Flutter Inspector 中的 widgets 树。顶部面板显示小部件树,而底部面板显示小部件的详细信息。
图 4-1
Flutter 检查器中的 Widgets 树
图 4-2 显示了 Flutter 检查器中的渲染树。根是一个RenderView
。
图 4-2
在 Flutter 检查器中渲染树
您想要访问与小部件树中的小部件相关的信息。
WidgetBuilder
函数有一个BuildContext
参数,用于访问小部件树中与小部件相关的信息。你可以在StatelessWidget.build()
和State.build()
方法中看到BuildContext
。
当构建小部件时,小部件在小部件树中的位置可能决定其行为,特别是当它有一个InheritedWidget
作为其祖先时。BuildContext
类提供了访问位置相关信息的方法;见表 4-1 。
表 4-1
构建上下文的方法
|
名字
|
描述
|
| --- | --- |
| ancestorInheritedElementForWidgetOfExactType
| 获取与给定类型的InheritedWidget
的最近祖先小部件对应的InheritedElement
。 |
| ancestorRenderObjectOfType
| 获取最近的祖先RenderObjectWidget
小部件的RenderObject
。 |
| ancestorStateOfType
| 获取最近祖先StatefulWidget
小部件的State
对象。 |
| rootAncestorStateOfType
| 获取最远祖先StatefulWidget
小部件的State
对象。 |
| ancestorWidgetOfExactType
| 获取最近的祖先Widget
。 |
| findRenderObject
| 获取小部件的当前RenderObject
。 |
| inheritFromElement
| 用给定的祖先InheritedElement
注册这个BuildContext
,以便当祖先的小部件改变时,这个BuildContext
被重建。 |
| inheritFromWidgetOfExactType
| 获取给定类型的最接近的InheritedWidget
并注册这个BuildContext
,以便当小部件改变时,这个BuildContext
被重建。 |
| visitAncestorElements
| 访问祖先元素。 |
| visitChildElements
| 访问子元素。 |
BuildContext
其实是Element
类的接口。在StatelessWidget.build()
和State.build()
方法中,BuildContext
对象表示当前小部件膨胀的位置。在清单 4-1 中,ancestorWidgetOfExactType()
方法用于获取类型Column
的祖先小部件。
class WithBuildContext extends StatelessWidget {
@override
Widget build(BuildContext context) {
Column column = context.ancestorWidgetOfExactType(Column);
return Text(column.children.length.toString());
}
}
Listing 4-1Use BuildContext
您希望创建一个没有可变状态的小部件。
从StatelessWidget
类扩展。
当使用一个 widget 来描述用户界面的一部分时,如果这个部分可以使用 widget 本身的配置信息和它所在的BuildContext
来完整描述,那么这个 widget 应该从StatelessWidget
扩展而来。当创建一个StatelessWidget
类时,您需要实现接受一个BuildContext
并返回一个Widget
的build()
方法。在清单 4-2 中,HelloWorld
类从StatelessWidget
类扩展而来,并在build()
方法中返回一个Center
小部件。
class HelloWorld extends StatelessWidget {
const HelloWorld({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: Text('Hello World!'),
);
}
}
Listing 4-2Example of StatelessWidget
您希望创建一个具有可变状态的小部件。
从StatefulWidget
类扩展。
如果用户界面的一部分可能动态变化,你需要从StatefulWidget
类扩展。对于由它们创建的State
对象中管理的状态,它们本身是不可变的。一个StatefulWidget
子类需要实现返回一个State
对象的createState()
方法。当状态改变时,State
对象要调用setState()
方法通知框架触发更新。在清单 4-3 中,_CounterState
类是Counter
小部件的State
对象。当按钮被按下时,值在setState()
方法中被更新,该方法更新_CounterState
小部件以显示新值。
class Counter extends StatefulWidget {
@override
_CounterState createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int value = 0;
@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Text('$value'),
RaisedButton(
child: Text('+'),
onPressed: () {
setState(() {
value++;
});
},
),
],
);
}
}
Listing 4-3Example of StatefulWidget
您希望沿着小部件树向下传播数据。
从InheritedWidget
类扩展。
当构建部件的子树时,您可能需要沿着部件树向下传播数据。例如,子树的根部件可能定义一些上下文数据,例如,从服务器检索的配置数据。子树中的其他小部件可能也需要访问上下文数据。一种可能的方法是将上下文数据添加到小部件的构造函数中,然后将数据作为子小部件的构造函数参数进行传播。这种解决方案的主要缺点是,您需要为子树中的所有小部件添加构造函数参数。尽管有些小部件可能实际上不需要数据,但是它们仍然需要将数据传递给它们的子部件。
更好的方法是使用InheritedWidget
类。BuildContext
类有一个inheritFromWidgetOfExactType()
方法来获取特定类型InheritedWidget
的最近实例。使用InheritedWidget
,您可以将上下文数据存储在一个InheritedWiget
实例中。如果小部件需要访问上下文数据,可以使用inheritFromWidgetOfExactType()
方法获取实例并访问数据。如果一个继承的小部件改变了状态,它将导致它的消费者重新构建。
在清单 4-4 ,ConfigWidget
类中有数据config
。静态的of()
方法为config
值获取最近的祖先ConfigWidget
实例。方法updateShouldNotify()
确定何时应该通知消费者窗口小部件。
class ConfigWidget extends InheritedWidget {
const ConfigWidget({
Key key,
@required this.config,
@required Widget child,
}) : assert(config != null),
assert(child != null),
super(key: key, child: child);
final String config;
static String of(BuildContext context) {
final ConfigWidget configWidget =
context.inheritFromWidgetOfExactType(ConfigWidget);
return configWidget?.config ?? ";
}
@override
bool updateShouldNotify(ConfigWidget oldWidget) {
return config != oldWidget.config;
}
}
Listing 4-4Example of InheritedWidget
在清单 4-5 中,ConfigUserWidget
类使用ConfigWidget.of()
方法获得config
值。
class ConfigUserWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text('Data is ${ConfigWidget.of(context)}');
}
}
Listing 4-5Use of ConfigWidget
在清单 4-6 中,ConfigWidget
实例的config
值为“Hello!”还有一个后裔ConfigUserWidget
的实例。
ConfigWidget(
config: 'Hello!',
child: Center(
child: ConfigUserWidget(),
),
);
Listing 4-6Complete example
你想显示一些文本。
使用Text
和RichText
小部件。
几乎所有的应用都需要向终端用户显示一些文本。Flutter 提供了几个与文本相关的类。Text
和RichText
是显示文本的两个小部件。事实上,Text
内部使用RichText
。Text
小部件的build()
方法返回一个RichText
实例。Text
和RichText
的区别在于Text
使用最近的封闭DefaultTextStyle
对象的样式,而RichText
需要显式样式。
文本有两个构造函数。第一个构造函数Text()
接受一个String
作为要显示的文本。另一个构造函数Text.rich()
接受一个TextSpan
对象来表示文本和样式。创建Text
小部件最简单的形式是Text('Hello world')
,它使用最近的封装DefaultTextStyle
对象的样式显示文本。Text()
和Text.rich()
构造函数都有几个命名参数来定制它们;参见表 4-2 。
表 4-2
Text()和 Text.rich()的命名参数
|
名字
|
类型
|
描述
|
| --- | --- | --- |
| style
| TextStyle
| 文本的样式。 |
| textAlign
| TextAlign
| 文本应如何水平对齐。 |
| textDirection
| TextDirection
| 文本的方向。 |
| locale
| Locale
| 基于 Unicode 选择字体的区域设置。 |
| softWrap
| bool
| 是否在软换行符处断开文本。 |
| overflow
| TextOverflow
| 如何处理文本溢出? |
| textScaleFactor
| double
| 缩放文本的因子。 |
| maxLines
| int
| 最大行数。如果文本超出限制,它将根据溢出中指定的策略被截断。 |
| semanticsLabel
| String
| 文本的语义标签。 |
TextAlign
是一个枚举类型,其值如表 4-3 所示。
表 4-3
文本对齐值
|
名字
|
描述
|
| --- | --- |
| left
| 将文本与其容器的左边缘对齐。 |
| right
| 将文本与其容器的右边缘对齐。 |
| center
| 将文本在其容器的中心对齐。 |
| justify
| 对于以软换行符结尾的文本行,拉伸这些行以填充容器的宽度;对于以硬换行符结尾的文本行,将它们向起始边缘对齐。 |
| start
| 将文本与其容器的前缘对齐。对于从左到右的文本,前导边缘是左边缘,而对于从右到左的文本,前导边缘是右边缘。 |
| end
| 将文本在其容器的后沿对齐。后缘与前缘相反。 |
建议始终使用TextAlign
值start
和end
,而不是left
和right
,以便更好地处理双向文本。TextDirection
是具有值ltr
和rtl
的枚举类型。TextOverflow
是一个枚举类型,其值如表 4-4 所示。
表 4-4
文本溢出值
|
名字
|
描述
|
| --- | --- |
| clip
| 剪裁溢出的文本。 |
| fade
| 将溢出的文本渐变为透明。 |
| ellipsis
| 在溢出的文本后添加省略号。 |
DefaultTextStyle
是一个InheritedWidget
,其属性style
、textAlign
、softWrap
、overflow
、maxLines
与表 4-2 中的命名参数含义相同。如果在构造函数Text()
和Text.rich()
中提供了一个命名参数,那么提供的值会覆盖最近的祖先DefaultTextStyle
对象中的值。清单 4-7 展示了几个使用Text
小部件的例子。
Text('Hello World')
Text(
'Bigger Bold Text',
style: TextStyle(fontWeight: FontWeight.bold),
textScaleFactor: 2.0,
);
Text(
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
maxLines: 1,
overflow: TextOverflow.ellipsis,
);
Listing 4-7Examples of Text
构造函数Text.rich()
将一个TextSpan
对象作为必需的参数。TextSpan
代表一段不可变的文本。TextSpan()
构造函数有四个命名参数;参见表 4-5 。TextSpan
组织是有等级制度的。一个TextSpan
对象可能有许多TextSpan
对象作为子对象。子元素可以覆盖父元素的样式。
表 4-5
TextSpan()的命名参数
|
名字
|
类型
|
描述
|
| --- | --- | --- |
| style
| TextStyle
| 文本和子对象的样式。 |
| text
| String
| 范围中的文本。 |
| children
| List
| 作为这个跨度的孩子。 |
| recognizer
| GestureRecognizer
| 接收事件的手势识别器。 |
清单 4-8 展示了使用Text.rich()
的例子。此示例使用不同的样式显示句子“快速的棕色狐狸跳过懒惰的狗”。
Text.rich(TextSpan(
style: TextStyle(
fontSize: 16,
),
children: [
TextSpan(text: 'The quick brown '),
TextSpan(
text: 'fox',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.red,
)),
TextSpan(text: ' jumps over the lazy '),
TextSpan(
text: 'dog',
style: TextStyle(
color: Colors.blue,
)),
],
));
Listing 4-8Example of Text.rich()
RichText
总是使用TextSpan
对象来表示文本和样式。RichText()
构造函数有一个TextSpan
类型的必需命名参数text
。它还有可选的命名参数textAlign
、textDirection
、softWrap
、overflow
、textScaleFactor
、maxLines
和locale
。这些可选的命名参数与表 4-2 中的Text()
构造函数含义相同。
显示在RichText
中的文本需要明确的样式。您可以使用DefaultTextStyle.of()
从BuildContext
对象中获取默认样式。这正是Text
在内部做的事情。Text
小部件获取默认样式,并与样式参数中提供的样式合并,然后创建一个RichText
,用一个TextSpan
包装文本和合并的样式。如果你发现你确实需要使用默认样式作为基础,你应该直接使用Text
而不是RichText
。清单 4-9 显示了一个使用RichText
的例子。
RichText(
text: TextSpan(
text: 'Level 1',
style: TextStyle(color: Colors.black),
children: [
TextSpan(
text: 'Level 2',
style: TextStyle(fontWeight: FontWeight.bold),
children: [
TextSpan(
text: 'Level 3',
style: TextStyle(color: Colors.red),
),
],
),
],
),
);
Listing 4-9Example of RichText
您希望显示的文本具有不同的样式。
用TextStyle
来描述风格。
TextStyle
描述应用于文本的样式。TextStyle()
构造函数有很多命名参数来描述样式;见表 4-6 。
表 4-6
TextStyle()的命名参数
|
名字
|
类型
|
描述
|
| --- | --- | --- |
| color
| Color
| 文本的颜色。 |
| fontSize
| Double
| 字体大小。 |
| fontWeight
| FontWeight
| 字体粗细。 |
| fontStyle
| FontStyle
| 字体变体。 |
| letterSpacing
| Double
| 每个字母之间的间隔。 |
| wordSpacing
| Double
| 每个单词之间的空格。 |
| textBaseLine
| TextBaseLine
| 将此文本范围与其父范围对齐的公共基线。 |
| height
| Double
| 文本的高度。 |
| locale
| Locale
| 用于选择区域特定标志符号的区域设置。 |
| foreground
| Paint
| 文本的前景。 |
| background
| Paint
| 文本的背景。 |
| shadows
| List
| 文本下面画的阴影。 |
| decoration
| TextDecoration
| 文本的修饰。 |
| decorationColor
| Color
| 文本装饰的颜色。 |
| decorationStyle
| TextDecorationStyle
| 文本装饰的样式。 |
| debugLabel
| String
| 调试样式的描述。 |
| fontFamily
| String
| 字体的名称。 |
| package
| String
| 如果字体是在包中定义的,与fontFamily
一起使用。 |
FontWeight
类定义值w100
、w200
、w300
、w400
、w500
、w600
、w700
、w800
、w900
。FontWeight.w100
最薄,而w900
最厚。FontWeight.bold
是FontWeight.w700
的别名,而FontWeight.normal
是FontWeight.w400
的别名。FontStyle
是具有两个值italic
和normal
的枚举类型。TextBaseline
是具有值alphabetic
和ideographic
的枚举类型。
TextDecoration
类定义了不同类型的文本装饰。您还可以使用构造函数TextDecoration.combine()
通过组合一系列TextDecoration
实例来创建一个新的TextDecoration
实例。例如,TextDecoration.combine([TextDecoration.underline, TextDecoration.overline])
实例在文本的上面和下面画线。表 4-7 显示了TextDecoration
中的常数。
表 4-7
文本装饰常数
|
名字
|
描述
|
| --- | --- |
| none
| 没有装饰。 |
| underline
| 在文本下面画一条线。 |
| overline
| 在文本上方画一条线。 |
| lineThrough
| 在文本中画一条线。 |
TextDecorationStyle
是一个枚举类型,其值如表 4-8 所示。TextDecorationStyle
定义由TextDecoration
创建的线条的样式。
表 4-8
文本修饰 Style values
|
名字
|
描述
|
| --- | --- |
| solid
| 画一条实线。 |
| double
| 画两条线。 |
| dotted
| 画一条虚线。 |
| dashed
| 画一条虚线。 |
| wavy
| 画一条正弦曲线。 |
清单 4-10 显示了一个使用TextDecoration
和TextDecorationStyle
的例子。
Text(
'Decoration',
style: TextStyle(
fontWeight: FontWeight.w900,
decoration: TextDecoration.lineThrough,
decorationStyle: TextDecorationStyle.dashed,
),
);
Listing 4-10Example of using TextDecoration and TextDecorationStyle
如果您想要创建一个更新了一些属性的TextStyle
实例的副本,那么使用copyWith()
方法。apply()
方法也创建了一个新的TextStyle
实例,但是它允许使用 factor 和 delta 更新一些属性。例如,命名参数fontSizeFactor
和fontSizeDelta
可以更新字体大小。用"fontSize * fontSizeFactor + fontSizeDelta"
计算fontSize
的更新值。您也可以使用相同的模式更新height
、letterSpacing
和wordSpacing
的值。对于fontWeight
,仅支持fontWeightDelta
。在清单 4-11 中,应用于文本的TextStyle
更新了fontSize
和decoration
的值。
Text(
'Scale',
style: DefaultTextStyle.of(context).style.apply(
fontSizeFactor: 2.0,
fontSizeDelta: 1,
decoration: TextDecoration.none,
),
);
Listing 4-11Update TextStyle
您想要显示从网络加载的图像。
使用带有图像 URL 的Image.network()
来加载和显示图像。
如果您在自己的服务器或其他地方托管了图像,您可以使用Image.network()
构造函数来显示它们。Image.network()
构造函数只需要加载图片的 URL。应该使用命名参数width
和height
为图像小部件指定特定的尺寸,或者将其放在设置严格布局约束的上下文中。这是因为加载图像时,图像的尺寸可能会改变。如果没有严格的大小限制,图像小部件可能会影响其他小部件的布局。在清单 4-12 中,图像小部件的大小由命名参数width
和height
指定。
Image.network(
'https://picsum.photos/400/300',
width: 400,
height: 300,
);
Listing 4-12Example of Image.network()
所有下载的图像都被缓存,不管 HTTP 头。这意味着所有 HTTP 缓存控制头都将被忽略。您可以使用缓存克星来强制刷新缓存的图像。例如,您可以向图像 URL 添加一个随机字符串。
如果加载图像需要额外的 HTTP 头,您可以指定类型为Map
的headers
参数来提供这些头。一个典型的用例是加载需要 HTTP 头进行身份验证的受保护图像。
如果一个图像不能覆盖一个盒子的整个区域,你可以使用类型ImageRepeat
的repeat
参数来指定图像如何重复。ImageRepeat
是一个枚举类型,其值如表 4-9 所示。默认值为noRepeat
。
表 4-9
图像重复值
|
名字
|
描述
|
| --- | --- |
| Repeat
| 在 x 和 y 两个方向重复。 |
| repeatX
| 仅在 x 方向重复。 |
| repeatY
| 仅在 y 方向重复。 |
| noRepeat
| 不重复。未覆盖的区域将是透明的。 |
在清单 4-13 中,图像被放入一个比图像大的SizedBox
中。通过使用ImageRepeat.repeat
,该框被该图像填充。
SizedBox(
width: 400,
height: 300,
child: Image.network(
'https://picsum.photos/300/200',
alignment: Alignment.topLeft,
repeat: ImageRepeat.repeat,
),
);
Listing 4-13Repeated images
你想用图标。
使用图标显示材质设计中的图标或社区中的图标包。
图标在移动应用中被广泛使用。与文本相比,图标在表达同样的语义时占用更少的屏幕空间。图标可以由字体符号或图像创建。Icon
小部件是用字体字形绘制的。一个字体字形用IconData
类描述。要创建一个IconData
实例,字体中该图标的 Unicode 码位是必需的。
Icons
类有许多预定义的IconData
常量用于材质设计中的图标(https://Material . io/tools/icons/)。例如,Icons.call
是名为“通话”的图标的IconData
常量。如果应用使用材质设计,那么这些图标可以开箱即用。CupertinoIcons
类有许多为 iOS 风格图标预定义的IconData
常量。
Icon()
构造函数已经命名了参数size
和color
来分别指定图标的大小和颜色。图标总是正方形的,宽度和高度都与大小相等。size 的默认值是 24。清单 4-14 创建一个大小为 100 的红色Icons.call
图标。
Icon(
Icons.call,
size: 100,
color: Colors.red,
);
Listing 4-14Example of Icon()
要使用流行的字体牛逼图标,可以使用包font_awesome_flutter
( https://pub.dartlang.org/packages/font_awesome_flutter
)。将包依赖关系添加到pubspec.yaml
文件后,您可以导入该文件以使用FontAwesomeIcons
类。类似于Icons
类,FontAwesomeIcons
类有许多IconData
常量,用于 Awesome 字体中的不同图标。清单 4-15 创建一个大小为 80 的蓝色FontAwesomeIcons.angry
图标。
Icon(
FontAwesomeIcons.angry,
size: 80,
color: Colors.blue,
);
Listing 4-15Use Font Awesome icon
你想使用带有文本的按钮。
使用按钮部件FlatButton
、RaisedButton
、OutlineButton
和CupertinoButton
。
Flutter 有不同类型的按钮用于材质设计和 iOS。这些按钮部件都有一个必需的参数onPressed
来指定按下时的处理函数。如果onPressed
处理器是null
,按钮被禁用。按钮的内容由类型为Widget
的参数child
指定。FlatButton
、RaisedButton
和OutlineButton
对触摸有不同的风格和行为反应:
一个FlatButton
有零海拔和没有可见的边界。它通过填充由highlightColor
指定的颜色来对触摸做出反应。
一个RaisedButton
有高程,用颜色填充。它通过将仰角增加到highlightElevation
来对触摸做出反应。
一个OutlineButton
有边界,初始高度为 0.0,背景透明。它对触摸的反应是用颜色使其背景不透明,并将其高度增加到highlightElevation
。
应该用在工具栏、对话框、卡片上,或者内嵌在其他有足够空间让按钮显而易见的地方。RaisedButton
s 应该用在使用空间不足以让按钮突出的地方。OutlineButton
是RaisedButton
和FlatButton
的杂交。OutlineButton
s 可以在FlatButton
s 和RaisedButton
s 都不合适的情况下使用。
如果你更喜欢 iOS 风格的按钮,你可以使用CupertinoButton
小部件。CupertinoButton
通过渐出和渐入对触摸做出反应。清单 4-16 展示了创建不同类型按钮的例子。
FlatButton(
child: Text('Flat'),
color: Colors.white,
textColor: Colors.grey,
highlightColor: Colors.red,
onPressed: () => {},
);
RaisedButton(
child: Text('Raised'),
color: Colors.blue,
onPressed: () => {},
);
OutlineButton(
child: Text('Outline'),
onPressed: () => {},
);
CupertinoButton(
child: Text('Cupertino'),
color: Colors.green,
onPressed: () => {},
);
Listing 4-16Different types of buttons
你想使用带有图标的按钮。
使用IconButton
控件、FlatButton.icon()
、RaisedButton.icon()
和OutlineButton.icon()
。
创建带有图标的按钮有两种方法。如果只有图标就够了,使用IconButton
widget。如果图标和文本都需要,使用构造函数FlatButton.icon()
、RaisedButton.icon()
或OutlineButton.icon()
。
IconButton
构造函数需要icon
参数来指定图标。FlatButton.icon()
、RaisedButton.icon()
和OutlineButton.icon()
分别使用参数icon
和label
来指定图标和文本。清单 4-17 显示了使用IconButton()
和RaisedButton.icon()
的例子。
IconButton(
icon: Icon(Icons.map),
iconSize: 50,
tooltip: 'Map',
onPressed: () => {},
);
RaisedButton.icon(
icon: Icon(Icons.save),
label: Text('Save'),
onPressed: () => [],
);
Listing 4-17Examples of IconButton() and RaisedButton.icon()
您希望添加占位符来表示稍后将添加的小部件。
使用占位符。
在实现一个应用的界面之前,你通常对这个应用的外观有一个基本的概念。您可以从将界面分解成许多小部件开始。您可以在开发过程中使用占位符来表示未完成的小部件,这样您就可以测试其他小部件的布局。例如,如果您需要创建两个小部件,一个显示在顶部,而另一个显示在底部。如果您选择首先创建底部小部件,并为顶部小部件使用占位符,您可以在所需位置看到底部小部件。
Placeholder()
构造函数接受命名参数color
、strokeWidth
、fallbackWidth
和fallbackHeight
。占位符被绘制为一个矩形和两条对角线。参数color
和strokeWidth
分别指定线条的颜色和宽度。默认情况下,占位符适合其容器。然而,如果占位符的容器是无界的,它使用给定的fallbackWidth
和fallbackHeight
来确定大小。fallbackWidth
和fallbackHeight
都有默认值400.0
。清单 4-18 显示了一个Placeholder
小部件的例子。
Placeholder(
color: Colors.red,
strokeWidth: 1,
fallbackHeight: 200,
fallbackWidth: 200,
);
Listing 4-18Example of Placeholder
微件在 Flutter 应用中无处不在。本章提供了对 Flutter 中控件的基本介绍,包括StatelessWidget
、StatefulWidget
和InheritedWidget
。本章还介绍了显示文本、图像、图标、按钮和占位符的常用基本小部件的用法。下一章将讨论 Flutter 中的布局。