可视化组件、常见的Widget

人间烟火/张佳伟版 Lv2
可视化组件的设置:在App主界面中 debugPaintSizeEnabled 设置为 true
1、实际上整个UI都是各种的组件堆叠而成的

一个 Flutter app 本身就是一个 widget,大多数 widgets 都有一个 build() 方法,在 app 的 build() 方法中实例化和返回一个 widget 会让它显示出来。

要点
Widgets 是用于构建 UI 的类。
Widgets 可以用于布局和展示 UI 元素。
通过组合简单的 widgets 来构建复杂的 widgets。
所有布局 widgets 都具有以下任一项:
一个 child 属性,它们只包含一个子项 —— 例如 Center 和 Container
一个 children 属性,它们包含多个子项 —— 例如 Row、Column、ListView 和 Stack

常见的布局组件 Row 和 Column

Row 和 Column 是水平和垂直布局的基本原始 widgets —— 这些低级 widgets 允许最大程度的自定义。
Flutter 还提供专门的、更高级别的 widgets,可能可以直接满足需求。
例如:
和 Row 相比你可能更喜欢 ListTile,这是一个易于使用的 widget,有属性可以设置头尾图标,最多可以显示 3 行文本;
和 Column 相比你也可能更喜欢 ListView,这是一种类似于列的布局,但如果其内容太长导致可用空间不够容纳时会自动滚动。

当空间容纳不下报错时

当某个布局太大而超出屏幕时,受影响的边缘会出现黄色和黑色条纹图案
通过使用 Expanded widget,可以调整 widgets 的大小以适合行或列。
要修复上一个图像行对其渲染框来说太宽的示例,可以用 Expanded widget 把每个图像包起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Image.asset('images/pic1.jpg'),
),
Expanded(
child: Image.asset('images/pic2.jpg'),
),
Expanded(
child: Image.asset('images/pic3.jpg'),
),
],
);
和兄弟项瓜分所占大小

也许你想要一个 widget 占用的空间是兄弟项的两倍。
为了达到这个效果,可以使用 Expanded widget 的 flex 属性,这是一个用来确定 widget 的弹性系数的整数。
默认的弹性系数为 1,以下代码将中间图像的弹性系数设置为 2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Image.asset('images/pic1.jpg'),
),
Expanded(
flex: 2,
child: Image.asset('images/pic2.jpg'),
),
Expanded(
child: Image.asset('images/pic3.jpg'),
),
],
);

默认情况下,行或列沿其主轴会占用尽可能多的空间,但如果要将子项紧密组合在一起,请将其 mainAxisSize 设置为 MainAxisSize.min。
这样他们就会挤在一起,如评分中的星星他们之间的距离等情况

组件间可以互相嵌套,可以说是一大特点吧

布局框架允许你根据需要在行和列内嵌套行和列。让我们看看以下布局的概述部分的代码:
(为了最大限度地减少高度嵌套的布局代码可能导致的视觉混乱,可以在变量和函数中实现 UI 的各个部分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
final stars = Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
const Icon(Icons.star, color: Colors.black),
const Icon(Icons.star, color: Colors.black),
],
);

final ratings = Container(
padding: const EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
stars,
const Text(
'170 Reviews',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w800,
fontFamily: 'Roboto',
letterSpacing: 0.5,
fontSize: 20,
),
),
],
),
);

一些常见的组件

标准 widgets

Container:向 widget 增加 padding、margins、borders、background color 或者其他的“装饰”。

GridView:将 widget 展示为一个可滚动的网格。

ListView:将 widget 展示为一个可滚动的列表。

Stack:将 widget 覆盖在另一个的上面。

Material widgets

Card:将相关信息整理到一个有圆角和阴影的盒子中。

ListTile:将最多三行的文本、可选的导语以及后面的图标组织在一行中。

Container

许多布局都可以随意的用 Container,它可以将使用了 padding 或者增加了 borders/margins 的 widget 分开。你可以通过将整个布局放到一个 Container 中,并且改变它的背景色或者图片,来改变设备的背景。

增加 padding、margins、borders

改变背景色或者图片

只包含一个子 widget,但是这个子 widget 可以是行、列或者是 widget 树的根 widget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Widget _buildDecoratedImage(int imageIndex) => Expanded(
child: Container(
decoration: BoxDecoration(
border: Border.all(width: 10, color: Colors.black38),
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
margin: const EdgeInsets.all(4),
child: Image.asset('images/pic$imageIndex.jpg'),
),
);


Container 还用来为每个图片添加圆角和外边距:
Widget _buildImageRow(int imageIndex) => Row(
children: [
_buildDecoratedImage(imageIndex),
_buildDecoratedImage(imageIndex + 1),
],
);
Stack

可以使用 Stack 在基础 widget(通常是图片)上排列 widget, widget 可以完全或者部分覆盖基础 widget。

摘要 (Stack)
用于覆盖另一个 widget
子列表中的第一个 widget 是基础 widget;后面的子项覆盖在基础 widget 的顶部
Stack 的内容是无法滚动的
你可以剪切掉超出渲染框的子项
在 CircleAvatar 的上面使用 Stack 覆盖 Container (在透明的黑色背景上展示它的 Text)。
Stack 使用 alignment 属性和 Alignment 让文本偏移。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Widget _buildStack() {
return Stack(
alignment: const Alignment(0.6, 0.6),
children: [
const CircleAvatar(
backgroundImage: AssetImage('images/pic.jpg'),
radius: 100,
),
Container(
decoration: const BoxDecoration(
color: Colors.black45,
),
child: const Text(
'Mia B',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
],
);
}

(这里孩子中只有两个,如果要控制多个,可以使用Positioned(child: child),相对于父系的绝对定位)

划线分隔线

Divider(),
有线高、粗细、长短、颜色等可供设置

Card

Card 的内容无法滚动
Material 库 中的 Card 包含相关有价值的信息,几乎可以由任何 widget 组成,但是通常和 ListTile 一起使用。 Card 只有一个子项,这个子项可以是列、行、列表、网格或者其他支持多个子项的 widget。默认情况下,Card 的大小是 0x0 像素。你可以使用 SizedBox 控制 card 的大小。
在 Flutter 中,Card 有轻微的圆角和阴影来使它具有 3D 效果。
改变 Card 的 elevation 属性可以控制阴影效果。例如,把 elevation 设置为 24,可以从视觉上更多的把 Card 抬离表面,使阴影变得更加分散。

ListTile

ListTile 是 Material 库 中专用的行 widget,它可以很轻松的创建一个包含三行文本以及可选的行前和行尾图标的行。 ListTile 在 Card 或者 ListView 中最常用,但是也可以在别处使用。

LayoutBuildery 与 SingleChildScrollView

添加带有SingleChildScrollView的LayoutBuilder。
在SingleChildScrollView中添加一个ConstrainedBox。
创建具有间隔项目的列。

首先创建一个LayoutBuilder。您需要提供一个具有两个参数的生成器回调函数:
LayoutBuilder提供的BuildContext。
父窗口小部件的BoxConstraints。
在这个配方中,您不会使用BuildContext,但在下一步中需要BoxConstraints。
在构建器函数中,返回一个SingleChildScrollView。这个小部件确保子小部件可以滚动,即使父容器太小。

1
2
3
4
5
LayoutBuilder(builder: (context, constraints) {
return SingleChildScrollView(
child: Placeholder(),
);
});

在此步骤中,添加一个ConstraintdBox作为SingleChildScrollView的子项。
ConstraintdBox小部件向其子部件施加额外的约束。
通过将minHeight参数设置为LayoutBuilder约束的maxHeight来配置约束。
这样可以确保子窗口小部件的最小高度等于LayoutBuilder约束提供的可用空间,即BoxConstraints的最大高度。

1
2
3
4
5
6
7
8
LayoutBuilder(builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: constraints.maxHeight),
child: Placeholder(),
),
);
});

最后,添加一个Column作为ConstraintdBox的子级。
要均匀地分配项目的空间,请将mainAxisAlignment设置为mainAxisAlignment.spaceBetween。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
LayoutBuilder(builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: constraints.maxHeight),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ItemWidget(text: 'Item 1'),
ItemWidget(text: 'Item 2'),
ItemWidget(text: 'Item 3'),
],
),
),
);
});

或者,如果您希望一个小部件比其他小部件占用更多空间,您可以使用间隔小部件来调整项目之间的间距,或者使用扩展小部件。
为此,您必须使用IntrinsicHeight小部件包装Column,这将迫使Column小部件将自己调整到最小高度,而不是无限扩展。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
LayoutBuilder(builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: constraints.maxHeight),
child: IntrinsicHeight(
child: Column(
children: [
ItemWidget(text: 'Item 1'),
Spacer(),
ItemWidget(text: 'Item 2'),
Expanded(
child: ItemWidget(text: 'Item 3'),
),
],
),
),
),
);
});
  • Title: 可视化组件、常见的Widget
  • Author: 人间烟火/张佳伟版
  • Created at: 2024-04-20 20:22:16
  • Updated at: 2024-04-21 21:28:36
  • Link: https://945912035.github.io/2024/04/20/2024-4-20/
  • License: This work is licensed under CC BY-NC-SA 4.0.
 Comments
On this page
可视化组件、常见的Widget