flutter常用widget

本文最后更新于 2024年7月25日 下午

flutter 开发中常用的 Widget 组件

Container

  • width 宽度
  • height 高度
  • margin 设置外间距
  • padding 设置内间距
  • alignment 对其方式
  • decoration Decoration 对 Container 进行修饰
    • gradient 渐变
    • boxShadow 阴影
  • transform 设置形变
  • constraints 设置 Contianer 最大、最小宽高
  • color 背景颜色
  • child 子组件

注意,这里不能同时设置 Colordecoration 中的渐变,否则会报错

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
31
32
33
34
35
36
37
38
Container(
// 设置宽
width: 100,
// 设置 外间距
margin: const EdgeInsets.all(9),
// 设置 内间距
padding: const EdgeInsets.all(8),
// 设置高
height: 100,
// 设置对其方式
alignment: Alignment.topLeft,
// 使用 Decoration 对 Container 进行修饰
decoration: const BoxDecoration(
// 渐变
gradient: RadialGradient(
//背景径向渐变
colors: [Colors.red, Colors.green],
center: Alignment.topLeft,
radius: .98,
),
// 阴影
boxShadow: [
//卡片阴影
BoxShadow(
color: Colors.black54,
offset: Offset(2.0, 2.0),
blurRadius: 4.0,
)
],
),
//卡片倾斜变换
transform: Matrix4.rotationZ(.2),
// 设置 Contianer 最大、最小宽高
// constraints: const BoxConstraints(maxWidth: 200, maxHeight: 200),
// Container 背景颜色
// color: Colors.green,
child: const Text("card"),
),

Row

  • textDirection 表示水平方向子组件的布局顺序(是从左往右还是从右往左),默认为系统当前Locale环境的文本方向(如中文、英语都是从左往右,而阿拉伯语是从右往左)
  • mainAxisSize 表示Row在主轴(水平)方向占用的空间,默认是MainAxisSize.max
  • mainAxisAlignment 表示Row在主轴元素的对其方式
  • verticalDirection 表示Row纵轴(垂直)的对齐方向,默认是VerticalDirection.down,表示从上到下。
  • crossAxisAlignment 表示子组件在纵轴方向的对齐方式,Row的高度等于子组件中最高的子元素高度,它的取值和MainAxisAlignment一样(包含start、end、 center三个值),不同的是crossAxisAlignment的参考系是verticalDirection,即verticalDirection值为VerticalDirection.down时crossAxisAlignment.start指顶部对齐,verticalDirection值为VerticalDirection.up时,crossAxisAlignment.start指底部对齐;而crossAxisAlignment.end和crossAxisAlignment.start正好相反;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 Row(
// 表示水平方向子组件的布局顺序(是从左往右还是从右往左),默认为系统当前Locale环境的文本方向(如中文、英语都是从左往右,而阿拉伯语是从右往左)
textDirection: TextDirection.ltr,
// 表示Row在主轴(水平)方向占用的空间,默认是MainAxisSize.max
mainAxisSize: MainAxisSize.min,
// 表示Row在主轴元素的对其方式
mainAxisAlignment: MainAxisAlignment.start,
// 表示Row纵轴(垂直)的对齐方向,默认是VerticalDirection.down,表示从上到下。
verticalDirection: VerticalDirection.up,
// 表示子组件在纵轴方向的对齐方式,Row的高度等于子组件中最高的子元素高度,它的取值和MainAxisAlignment一样(包含start、end、 center三个值),不同的是crossAxisAlignment的参考系是verticalDirection,即verticalDirection值为VerticalDirection.down时crossAxisAlignment.start指顶部对齐,verticalDirection值为VerticalDirection.up时,crossAxisAlignment.start指底部对齐;而crossAxisAlignment.end和crossAxisAlignment.start正好相反;
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(10),
child: Text("你好"),
),
Padding(
padding: EdgeInsets.all(5),
child: Text("HELLO"),
)
],
),

Column

  • mainAxisAlignment Column对其方式
  • mainAxisSize Column元素占据空间 max, min
  • crossAxisAlignment 表示Column子组件在纵轴方向的对齐方式
  • verticalDirection 表示Column纵轴(垂直)的对齐方向,默认是VerticalDirection.down,表示从上到下。
1
2
3
4
5
6
7
8
9
10
11
Column(
// Column对其方式
mainAxisAlignment: MainAxisAlignment.start,
// Column元素占据空间 max, min
mainAxisSize: MainAxisSize.max,
// 表示Column子组件在纵轴方向的对齐方式
crossAxisAlignment: CrossAxisAlignment.start,
// 表示Column纵轴(垂直)的对齐方向,默认是VerticalDirection.down,表示从上到下。
verticalDirection: VerticalDirection.up,
children: [Text('你好'), Text("世界")],
)

SafeArea

安全距,适配与齐刘海设备,防止顶部、底部内容区被遮盖

1
2
3
4
5
6
7
8
9
10
11
12
SafeArea(
child: Column(
// Column对其方式
mainAxisAlignment: MainAxisAlignment.start,
// Column元素占据空间 max, min
mainAxisSize: MainAxisSize.max,
// 表示Column子组件在纵轴方向的对齐方式
crossAxisAlignment: CrossAxisAlignment.start,
// 表示Column纵轴(垂直)的对齐方向,默认是VerticalDirection.down,表示从上到下。
verticalDirection: VerticalDirection.up,
children: [Text('你好'), Text("世界")],
));

Flex

Flex 布局,根据使用场景的不同,可以结合 Row 或者 Column 使用;Expanded 只能作为 Flex 的孩子(否则会报错)。

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
Column(
children: [
const Padding(padding: EdgeInsets.fromLTRB(0, 10, 0, 0)),
Flex(
direction: Axis.horizontal,
children: [
Expanded(
flex: 1,
child: Container(
decoration: const BoxDecoration(color: Colors.red),
alignment: Alignment.topLeft,
child: const Text("data1"),
)),
Expanded(
flex: 2,
child: Container(
decoration: const BoxDecoration(color: Colors.blue),
child: const Text("data2"),
),
),
Expanded(
flex: 1,
child: Container(
alignment: Alignment.topLeft,
decoration: const BoxDecoration(color: Colors.amberAccent),
child: const Text("data3"),
),
)
],
),
const Padding(padding: EdgeInsets.fromLTRB(0, 10, 0, 0)),
Flex(
direction: Axis.horizontal,
children: [
Expanded(
flex: 1,
child: Container(
decoration: const BoxDecoration(color: Colors.green),
child: const Text("flex 1"),
),
)
],
)
],
)

Wrap

Wrap的作用在于,当子使用Row在进行横向或者使用Column进行纵向排列时,子节点过宽。会导致长度溢出,这时候使用Wrap很好的解决了此问题。

  • spacing 主轴方向子widget的间距
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
31
32
33
34
35
36
Wrap(
direction: Axis.horizontal,
alignment: WrapAlignment.center,
// 主轴方向子widget的间距
spacing: 20,
children: [
Container(
alignment: Alignment.topLeft,
color: Colors.red,
width: 80,
height: 80,
child: const Text("data1"),
),
Container(
alignment: Alignment.topLeft,
width: 80,
height: 80,
color: Colors.amberAccent,
child: const Text("data2"),
),
Container(
alignment: Alignment.topLeft,
width: 80,
height: 80,
color: Colors.black38,
child: const Text("data3"),
),
Container(
alignment: Alignment.topLeft,
width: 80,
height: 80,
color: Colors.blueGrey,
child: const Text("data4"),
)
],
)

Stack

  • ConstrainedBox

    • constraints 铺满整个屏幕
  • Stack

    • alignment 此参数决定如何去对齐没有定位(没有使用Positioned)或部分定位的子组件。所谓部分定位,在这里特指没有在某一个轴上定位:left、right为横轴,top、bottom为纵轴,只要包含某个轴上的一个定位属性就算在该轴上有定位。
    • fit 此参数用于确定没有定位的子组件如何去适应Stack的大小。StackFit.loose表示使用子组件的大小,StackFit.expand表示扩伸到Stack的大小。
    • textDirection 和Row、Wrap的textDirection功能一样,都用于确定alignment对齐的参考系,即:textDirection的值为TextDirection.ltr,则alignment的start代表左,end代表右,即从左往右的顺序;textDirection的值为TextDirection.rtl,则alignment的start代表右,end代表左,即从右往左的顺序。
  • Positioned

    • left、top 、right、 bottom分别代表离Stack左、上、右、底四边的距离。width和height用于指定需要定位元素的宽度和高度。注意,Positioned的width、height 和其他地方的意义稍微有点区别,此处用于配合left、top 、right、 bottom来定位组件,举个例子,在水平方向时,你只能指定left、right、width三个属性中的两个,如指定left和width后,right会自动算出(left+width),如果同时指定三个属性则会报错,垂直方向同理。
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
ConstrainedBox(
// 铺满整个屏幕
constraints: const BoxConstraints.expand(),
// 使用层级布局,实现元素叠放效果
child: Stack(
alignment: Alignment.center,
children: [
Container(
alignment: Alignment.topLeft,
child: Text("Hello world", style: TextStyle(color: Colors.white)),
color: Colors.red,
),
// 左定位
const Positioned(
left: 18.0,
child: Text("I am Jack"),
),
// 左上定位
const Positioned(
top: 18.0,
left: 20.0,
child: Text(
"Your friend",
style: TextStyle(color: Colors.blueGrey),
),
)
],
),
)

Align

  • alignment 对其方式,可以使用具体值Alignment(1,1),也可以使用内容方式,比如 Alignment.topRight
1
2
3
4
5
Align(
// 对其方式,可以使用具体值Alignment(1,1),也可以使用内容方式,比如 Alignment.topRight
alignment: Alignment.topRight,
child: FlutterLogo(),
)
  • FractionalOffset
    • FractionalOffset 继承自 Alignment,它和 Alignment唯一的区别就是坐标原点不同!- FractionalOffset 的坐标原点为矩形的左侧顶点,这和布局系统的一致,所以理解起来会比较容易。
    • FractionalOffset(.2, 0.2) 具体数值
    • FractionalOffset的坐标转换公式为:
1
实际偏移 = (FractionalOffse.x * (parentWidth - childWidth), FractionalOffse.y * (parentHeight - childHeight))
1
2
3
4
5
Align(
// alignment: FractionalOffset.topLeft,
alignment: FractionalOffset(.2, 0.2)
child: FlutterLogo(),
)
1
2
3
4
5
6
7
8
9
10
11
12
Container(
width: 90,
height: 90,
decoration: const BoxDecoration(
color: Colors.limeAccent,
),
child: const Align(
// 对其方式,可以使用具体值Alignment(1,1),也可以使用内容方式,比如 Alignment.topRight
alignment: Alignment.topRight,
child: FlutterLogo(),
),
)

Button

  • ElevatedButton 普通按钮,ElevatedButton.icon 带icon的普通按钮
  • OutlinedButton 空心按钮,OutlinedButton.icon 带icon的空心按钮
  • TextButton 文本按钮, TextButton.icon 带icon的文本按钮
  • IconButton 普通的icon按钮
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
ElevatedButton.icon(
onPressed: () {},
label: const Text("tips"),
icon: const Icon(Icons.push_pin),
),
OutlinedButton.icon(
onPressed: () {},
label: const Text("发送"),
icon: const Icon(Icons.construction),
),
TextButton.icon(
onPressed: () {},
label: const Text("发送"),
icon: const Icon(Icons.search_off_outlined),
),
IconButton(onPressed: () {}, icon: const Icon(Icons.stadium))
],
)

Image

  • Image.network 使用网络图片
  • IMage.asset 使用本地图片
    • width、height:用于设置图片的宽、高,当不指定宽高时,图片会根据当前父容器的限制,尽可能的显示其原始大小,如果只设置width、height的其中一个,那么另一个属性默认会按比例缩放,但可以通过下面介绍的fit属性来指定适应规则
    • fit: 该属性用于在图片的显示空间和图片本身大小不同时指定图片的适应模式。适应模式是在BoxFit中定义,它是一个枚举类型,有如下值
    • fill: 会拉伸填充满显示空间,图片本身长宽比会发生变化,图片会变形。
    • cover: 会按图片的长宽比放大后居中填满显示空间,图片不会变形,超出显示空间部分会被剪裁。
    • contain: 这是图片的默认适应规则,图片会在保证图片本身长宽比不变的情况下缩放以适应当前显示空间,图片不会变形。
    • fitWidth: 图片的宽度会缩放到显示空间的宽度,高度会按比例缩放,然后居中显示,图片不会变形,超出显示空间部分会被剪裁。
    • fitHeight: 图片的高度会缩放到显示空间的高度,宽度会按比例缩放,然后居中显示,图片不会变形,超出显示空间部分会被剪裁。
    • none: 图片没有适应策略,会在显示空间内显示图片,如果图片比显示空间大,则显示空间只会显示图片中间部分。

注意:使用本地图片时候,需要现在 pubspec.yaml中配置, 如下所示:

1
2
assets:
- images/avatar.png

本地图片

1
2
3
4
5
6
Image(
image: AssetImage("images/avatar.png"),
width: 100.0,
color: Colors.blue,
colorBlendMode: BlendMode.difference,
);

网络图片

1
2
3
4
5
6
7
Image.network(
"https://avatars2.githubusercontent.com/u/20411648?s=460&v=4",
width: 100.0,
fit: BoxFit.contain,
color: Colors.red,
colorBlendMode: BlendMode.difference,
)

Switch

  • onChanged 切换时状态改变的回调函数
  • value 定义改变状态的数值
1
2
3
4
5
6
7
8
9
bool _switchSelected = true;
Switch(
onChanged: (event) {
setState(() {
_switchSelected = event;
});
},
value: _switchSelected,
)

Checkbox

  • onChanged 切换时状态改变的回调函数
  • value 定义改变状态的数值
1
2
3
4
5
6
7
8
9
bool _checkboxSelected = true;
Checkbox(
onChanged: (event) {
setState(() {
_checkboxSelected = event!;
});
},
value: _checkboxSelected,
)

TextField

  • controller:编辑框的控制器,通过它可以设置/获取编辑框的内容、选择编辑内容、监听编辑文本改变事件。大多数情况下我们都需要显式提供一个controller来与文本框交互。如果没有提供controller,则TextField内部会自动创建一个。

  • focusNode:用于控制TextField是否占有当前键盘的输入焦点。它是我们和键盘交互的一个句柄(handle)。

  • InputDecoration:用于控制TextField的外观显示,如提示文本、背景颜色、边框等。

  • keyboardType: 用户设置该输入框默认的键盘输入类型

    • text: 文本输入键盘
    • multiline: 多行文本,需要和 maxLines 配合使用(设为null或者大于1)
    • number: 数字,弹出数字键盘
    • phone: 优化后的电话号码输入键盘;会弹出数字键盘并显示“*#”
    • datetime: 优化后的日期输入键盘;Android上会显示“:-”
    • emailAddress: 优化后的电子邮件地址;会显示“@.”
    • url: 优化后的url输入键盘;会显示 “/.”
  • textInputAction:键盘动作按钮图标(搜索),它是一个枚举值,有多个可选值,全部的取值列表读者可以查看API文档,下面是当值为TextInputAction.search时,原生Android系统下键盘样式(搜索)。

  • style:设置样式

    1
    style: const TextStyle(fontSize: 16.0, fontWeight: FontWeight.w500, color: Colors.amber)
  • textAlign:文本对其方式

    1
    textAlign: TextAlign.left,
  • autofocus: 自动获取焦点

  • obscureText:设置为true输入时输入值以.显示,密码框可以用这个实现

  • maxLines:输入值最长行数,默认null即不限制输入换行

  • maxLength:最长输入字符长度

  • toolbarOptions:引用官方的原话如下: ( ‘toolbarOptions’ is deprecated and shouldn’t be used. Use contextMenuBuilder instead. This feature was deprecated after v3.3.0-0.5.pre.
    Try replacing the use of the deprecated member with the replacement. ) 改用 contextMenuBuilder 返回一个 Widget 实例,例如下面写法:

    1
    2
    3
    4
    5
    // 使用 contextMenuBuilder 代替 toolbarOptions
    contextMenuBuilder:
    (BuildContext context, EditableTextState editableTextState) {
    return const Text("data");
    },
  • onChanged:输入时会结构的回调,会监听用户输入;还可以使用 controller 实例获取输入的值。

  • inputFormatters:对输入的值进行校验,参考如下代码

    1
    2
    3
    4
    5
    6
    inputFormatters: [
    // 限制输入类型
    FilteringTextInputFormatter.allow(RegExp('[a-zA-Z]')),
    // 限制输入长度
    LengthLimitingTextInputFormatter(8)
    ],
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

// 定义 TextField Controller 为接收 TextField 实例产生的值
final TextEditingController _textUsernameController = TextEditingController();
final TextEditingController _textPasswordController = TextEditingController();

TextField(
// 自动获取焦点
autofocus: true,
// 用于获取用户输入值
controller: _textUsernameController,
// 定义输入输入类型
keyboardType: TextInputType.emailAddress,
// 定义键盘确认按钮
textInputAction: TextInputAction.next,
// textFiled 样式
style: const TextStyle(
fontSize: 16.0, fontWeight: FontWeight.w500, color: Colors.amber),
// textFiled 文本对其方式
textAlign: TextAlign.left,
// 最多可以输入几行,默认 Null 多行
maxLines: 1,
// 限制输入长度数
maxLength: 80,
// contextMenuBuilder:
// (BuildContext context, EditableTextState editableTextState) {
// return const Text("data-----");
// },
// 获取用户输入值,同理可以使用 controller
onChanged: (value) {
print("onChangeValue ${value}");
},
// TextFiled 输入值校验
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp('[a-zA-Z]')),
LengthLimitingTextInputFormatter(8)
],
// TextFiled 输入框外观定义
decoration: const InputDecoration(
labelText: "用户名",
hintText: "用户名或邮箱",
prefixIcon: Icon(Icons.person)),
),
TextField(
obscureText: true,
controller: _textPasswordController,
decoration: const InputDecoration(
labelText: "密码", hintText: "请输入密码", prefixIcon: Icon(Icons.lock)),
)

// 输入框值输出
ElevatedButton.icon(
onPressed: () {
// 输出
print(_textUsernameController.text);
},
label: const Text("获取数据"),
icon: const Icon(Icons.generating_tokens),
)

Form

  • key 使用formKey 获取输入框的值
1
2
// 定义 _formKey 实例
GlobalKey _formKey = GlobalKey<FormState>();
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
Column(
children: [
Form(
key: _formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Column(
children: [
TextFormField(
autofocus: true,
keyboardType: TextInputType.text,
textAlign: TextAlign.left,
maxLines: 1,
decoration: const InputDecoration(
labelText: "用户名",
hintText: "请输入用户名",
prefixIcon: Icon(Icons.person_3_outlined)),
validator: (v) {
// 校验
return v!.trim().isNotEmpty ? null : '用户名不能为空';
},
),
TextFormField(
obscureText: true,
keyboardType: TextInputType.text,
textAlign: TextAlign.left,
maxLines: 1,
decoration: const InputDecoration(
labelText: "用户密码",
hintText: "请输入密码",
prefixIcon: Icon(Icons.lock_outline)),
validator: (v) {
// 校验
return v!.trim().isNotEmpty ? null : '密码不能为空';
},
),
Padding(
padding: const EdgeInsets.only(top: 20.0),
child: Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () {
var _formState =
_formKey.currentState as FormState;
// 校验数据
if (_formState.validate()) {
_formState.save();
}
},
child: const Text("提交数据")))
],
),
)
],
))
],
)

容器类

Padding
  • EdgeInsets
    • fromLTRB(double left, double top, double right, double bottom):分别指定四个方向的填充。
    • all(double value) : 所有方向均使用相同数值的填充。
    • only({left, top, right ,bottom }):可以设置具体某个方向的填充(可以同时指定多个方向)。
    • symmetric({ vertical, horizontal }):用于设置对称方向的填充,vertical指top和bottom,horizontal指left和right
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
Padding(
padding: const EdgeInsets.only(top: 20),
child: Container(
// 盒子外观样式
decoration: BoxDecoration(
// 盒子圆角
borderRadius: BorderRadius.circular(3.0),
// 盒子渐变
gradient:
LinearGradient(colors: [Colors.red, Colors.orange.shade700]),
// 盒子背景阴影
boxShadow: const [
BoxShadow(
color: Colors.black54,
offset: Offset(2.0, 2.0),
blurRadius: 4.0)
]),
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 80, vertical: 18),
child: Text(
"我是按钮",
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.w800, color: Colors.white),
),
),
),
)
BoxDecoration
  • borderRadius:容器的圆角值
  • gradient:渐变
  • boxShadow:阴影
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
Padding(
padding: const EdgeInsets.only(top: 20),
child: Container(
// 盒子外观样式
decoration: BoxDecoration(
// 盒子圆角
borderRadius: BorderRadius.circular(3.0),
// 盒子渐变
gradient:
LinearGradient(colors: [Colors.red, Colors.orange.shade700]),
// 盒子背景阴影
boxShadow: const [
BoxShadow(
color: Colors.black54,
offset: Offset(2.0, 2.0),
blurRadius: 4.0)
]),
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 80, vertical: 18),
child: Text(
"我是按钮",
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.w800, color: Colors.white),
),
),
),
)


flutter常用widget
https://dev.dgdream.online/2024/07/24/flutter常用widget/
作者
执念
发布于
2024年7月24日
更新于
2024年7月25日
许可协议