Flutter Widget基础

Posted by アライさん on 2019年11月26日

一、 Container(容器组件)

可以用来控制大小、背景颜色、边框、阴影、内外边距和内容排列方式等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Container({
Key key,
double width,
double height,
this.margin,
this.padding,
Color color,
this.alignment,
BoxConstraints constraints,
Decoration decoration,
this.foregroundDecoration,
this.transform,
this.child,
})

1.1 width,height,margin,padding

  • EdgeInsets.all(value): 用于设置4个方向一样的值;
  • EdgeInsets.only(left: val1, top: val2, right: val3, bottom: val4): 可以单独设置某个方向的值;
  • EdgeInsets.symmetric(horizontal: val1, vertical: val2): 用于设置水平/垂直方向上的值;
  • EdgeInsets.fromLTRB(left, top, right, bottom): 按照左上右下的顺序设置4个方向的值。

1.2 color

可以用Color(0xFFFF0000)或Colors.red来表示。

1.3 alignment

决定Container组件的子组件将以何种方式进行排

  • Alignment.topLeft: 左上
  • Alignment.topCenter: 上中
  • Alignment.topRight: 右上
  • Alignment.centerLeft: 左中
  • Alignment.center: 居中
  • Alignment.centerRight: 右中
  • Alignment.bottomLeft: 左下
  • Alignment.bottomCenter: 下中
  • Alignment.bottomRight: 右下

1.4 constraints

BoxConstraints约束盒限制容器的宽高

1
2
3
4
5
6
7
// 容器的大小将被限制在[100*100 ~ 200*200]内
BoxConstraints(
minWidth: 100,
maxWidth: 200,
minHeight: 100,
maxHeight: 200,
)

1.5 decoration装饰器

通过它你可以设置边框,阴影,渐变,圆角等常用属性。BoxDecoration继承自Decoration类,因此我们通常会生成一个BoxDecoration实例来设置这些属性。

  • 边框
    可以用Border.all构造函数直接生成4条边框,也可以用Border构造函数单独设置不同方向上的边框。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 同时设置4条边框:1px粗细的黑色实线边框
    BoxDecoration(
    border: Border.all(color: Colors.black, width: 1, style: BorderStyle.solid)
    )
    // 设置单边框:上边框为1px粗细的黑色实线边框,右边框为1px粗细的红色实线边框
    BoxDecoration(
    border: Border(
    top: BorderSide(color: Colors.black, width: 1, style: BorderStyle.solid),
    right: BorderSide(color: Colors.red, width: 1, style: BorderStyle.solid),
    ),
    )
  • 阴影
    可以指定x,y,blur,spread,color等属性
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    BoxDecoration(
    boxShadow: [
    BoxShadow(
    offset: Offset(0, 0),
    blurRadius: 6,
    spreadRadius: 10,
    color: Color.fromARGB(20, 0, 0, 0),
    ),
    ],
    )
  • 渐变
    如果你不想容器的背景颜色是单调的,可以尝试用gradient属性。Flutter同时支持线性渐变和径向渐变
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 从左到右,红色到蓝色的线性渐变
    BoxDecoration(
    gradient: LinearGradient(
    begin: Alignment.centerLeft,
    end: Alignment.centerRight,
    colors: [Colors.red, Colors.blue],
    ),
    )
    // 从中心向四周扩散,红色到蓝色的径向渐变
    BoxDecoration(
    gradient: RadialGradient(
    center: Alignment.center,
    colors: [Colors.red, Colors.blue],
    ),
    )
  • 圆角
    BorderRadius.circular构造函数来同时设置4个角的圆角,或是BorderRadius.only构造函数来单独设置某几个角的圆角
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 同时设置4个角的圆角为5
    BoxDecoration(
    borderRadius: BorderRadius.circular(5),
    )
    // 设置单圆角:左上角的圆角为5,右上角的圆角为10
    BoxDecoration(
    borderRadius: BorderRadius.only(
    topLeft: Radius.circular(5),
    topRight: Radius.circular(10),
    ),
    )

1.6 transform平移,缩放、旋转和倾斜

  • translationValues(x, y, z): 平移x, y, z;
  • rotationX(radians): x轴旋转radians弧度;
  • rotationY(radians): y轴旋转radians弧度;
  • rotationZ(radians): z轴旋转radians弧度;
  • skew(alpha, beta): x轴倾斜alpha度,y轴倾斜beta度;
  • skewX(alpha): x轴倾斜alpha度;
  • skewY(beta): y轴倾斜beta度;

二、 Row/Column(列/行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Row({
Key key,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
MainAxisSize mainAxisSize = MainAxisSize.max,
List<Widget> children = const <Widget>[],
})

Column({
Key key,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
MainAxisSize mainAxisSize = MainAxisSize.max,
List<Widget> children = const <Widget>[],
})

2.1 mainAxisAlignment(主轴排列方式)

  • MainAxisAlignment.start
  • MainAxisAlignment.end
  • MainAxisAlignment.center
  • MainAxisAlignment.spaceBetween
  • MainAxisAlignment.spaceAround
  • MainAxisAlignment.spaceEvenly

    2.2 CrossAxisAlignment(次轴排列方式)

    Column次轴为横向,默认居中,不会撑满。需要指定CrossAxisAlignment.stretch
  • CrossAxisAlignment.start
  • CrossAxisAlignment.end
  • CrossAxisAlignment.center
  • CrossAxisAlignment.stretch
  • CrossAxisAlignment.baseline

    2.3 mainAxisSize主轴尺寸

    主轴方向上是否撑满还是最小模式。
    MainAxisSize.min和MainAxisSize.max

三、 Stack(层叠布局)、Positioned(绝对位置布局)

Stack为容器,Positioned组件通过left,top ,right,bottom四个方向上的属性值来决定其在父容器中的位置。
IndexedStack和Stack相似,通过index属性控制第几个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
Container(
height: 100,
color: Colors.yellow,
child: Stack(
children: <Widget>[
Positioned(
left: 10,
top: 10,
child: Container(width: 10, height: 10, color: Colors.red),
),
Positioned(
right: 10,
top: 10,
child: Container(width: 10, height: 10, color: Colors.red),
),
Positioned(
left: 10,
bottom: 10,
child: Container(width: 10, height: 10, color: Colors.red),
),
Positioned(
right: 10,
bottom: 10,
child: Container(width: 10, height: 10, color: Colors.red),
),
],
),
)

四、 Text(文本组件)

1
2
3
4
5
6
7
8
9
const Text(
this.data, {
Key key,
this.style,
this.textAlign,
this.softWrap,
this.overflow,
this.maxLines,
})
  • data: 显示的文本信息;
  • style: 文本样式,Flutter提供了一个TextStyle类,最常用的fontSize,fontWeight,color,backgroundColor和shadows等属性都是通过它设置的;
  • textAlign: 文字对齐方式,常用可选值有TextAlign的left,right,center和justify;
  • softWrap: 文字是否换行;
  • overflow: 当文字溢出的时候,以何种方式处理(默认直接截断)。可选值有TextOverflow的clip,fade,ellipsis和visible;
  • maxLines: 当文字超过最大行数还没显示完的时候,就会根据overflow属性决定如何截断处理。

富文本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Text.rich(TextSpan(
children: [
TextSpan(
'¥',
style: TextStyle(
fontSize: 12,
color: Color(0xFFFF7528),
),
),
TextSpan(
'258',
style: TextStyle(
fontSize: 15,
color: Color(0xFFFF7528),
),
),
]
))

五、 Image(图片组件)

1
2
3
4
5
6
7
8
9
Image({
Key key,
@required this.image,
this.width,
this.height,
this.color,
this.fit,
this.repeat = ImageRepeat.noRepeat,
})
  • image: 图片源,最常用到主要有两种(AssetImage和NetworkImage)。使用AssetImage之前,需要在pubspec.yaml文件中声明好图片资源,然后才能使用;而NextworkImage指定图片的网络地址即可,主要是在加载一些网络图片时会用到;
  • width: 图片宽度;
  • height: 图片高度;
  • color: 图片的背景颜色,当网络图片未加载完毕之前,会显示该背景颜色;
  • fit: 当我们希望图片根据容器大小进行适配而不是指定固定的宽高值时,可以通过该属性来实现。其可选值有BoxFit的fill,contain,cover,fitWidth,fitHeight,none和scaleDown;
  • repeat: 决定当图片实际大小不足指定大小时是否使用重复效果。
    加载方式:
  • Image : 从ImageProvider获取图片
  • Image.asset:加载资源图片
  • Image.file:加载本地图片
  • Image.network:加载网络图片
  • Image.memory:加载Uint8List资源图片
    BoxFit填充模式:
  • BoxFit.fill:拉伸充满变形
  • BoxFit.contain:全图显示,原比例,不需充满
  • BoxFit.cover:等比拉伸,充满,可能裁剪
  • BoxFit.none:原始大小,图太大只显示一点
  • BoxFit.scaleDown:类似contain,可小不可大
  • BoxFit.fitWidth:等比宽度充满,可能裁剪可能拉伸
  • BoxFit.fitHeight:等比高度充满,可能裁剪可能拉伸
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Image(
    image: NetworkImage('https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1402367109,4157195964&fm=27&gp=0.jpg'),
    width: 100,
    height: 100,
    )
    Image.network(
    'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1402367109,4157195964&fm=27&gp=0.jpg',
    width: 100,
    height: 100,
    )

六、 Icon(图标组件)

放大不会失真

1
2
3
4
5
6
const Icon(
this.icon, {
Key key,
this.size,
this.color,
})
  • icon: 图标类型;
  • size: 图标大小;
  • color: 图标颜色。

七、 Wrap(自动换行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
return Wrap(
spacing: 8.0,//chip之间的间距
runSpacing: 4.0,//行或列间距
children: <Widget>[
Chip(
label: Text('西门吹雪'),
avatar: CircleAvatar(
backgroundColor: Colors.lightGreen.shade800,
child: Text('西门', style: TextStyle(fontSize: 10.0),
),
),
),
Chip(
avatar: CircleAvatar(
backgroundColor: Colors.lightBlue.shade900,
child: Text('一郎', style: TextStyle(fontSize: 10.0),
),
),
label: Text('司空摘星'),
),
],
);

八、 Form(表格)

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
Form(
key: loginKey,
child: new Column(
children: <Widget>[
new TextFormField(
decoration: new InputDecoration(
labelText: '请输入用户名',
),
onSaved: (value) {},
onFieldSubmitted: (value) {},
),
new TextFormField(
decoration: new InputDecoration(
labelText: '请输入密码',
),
obscureText: true,
validator: (value) {
return value.length < 6 ? "密码长度不够6位" : null;
},
onSaved: (value) {},
),
new SizedBox(
width: 340.0,
height: 42.0,
child: new RaisedButton(
child: new Text(
'登录',
style: TextStyle(
fontSize: 18.0,
),
),
),
),
],
),
);

九、 GridView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
return GridView.count(
crossAxisCount: 3,
primary: false,
padding: const EdgeInsets.all(20.0),
crossAxisSpacing: 30.0,
children: <Widget>[
const Text('gridview'),
const Text('gridview'),
const Text('gridview'),
const Text('gridview'),
const Text('gridview'),
const Text('gridview'),
const Text('gridview'),
const Text('gridview'),
const Text('gridview'),
const Text('gridview'),
const Text('gridview'),
const Text('gridview'),
const Text('gridview'),
],
);

十、 ListVIew

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
ListView.separated(
separatorBuilder: (context, index) => Divider(
height: 0.5,
),
itemCount: rssList.length,
itemBuilder: (context, index) {
if (index == rssList.length - 1) {
//小于总数
//获取数据
return Container(
padding: const EdgeInsets.all(16.0),
alignment: Alignment.center,
child: SizedBox(
width: 24.0,
height: 24.0,
child: CircularProgressIndicator(
strokeWidth: 2.0,
),
),
);
} else {
return ListTile(
title: Text(
'',
style: TextStyle(
color: Colors.black87, fontSize: 16.0),
),
);
}
},
);

十一、 CupertinoAlertDialog

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
import 'package:flutter/cupertino.dart';
return CupertinoAlertDialog(
title: Text('提示'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('是否删除?'),
Text('一旦删除数据不可恢复'),
],
),
),
actions: <Widget>[
new Container(
decoration: BoxDecoration(
border: Border(
right: BorderSide(color: Colors.grey[200], width: 1.0),
top: BorderSide(color: Colors.grey[200], width: 1.0))),
child: FlatButton(
child: new Text("确定"),
onPressed: () {
Navigator.pop(context);
},
),
),
new Container(
decoration: BoxDecoration(
border:
Border(top: BorderSide(color: Colors.grey[200], width: 1.0))),
child: FlatButton(
child: new Text("取消"),
onPressed: () {
Navigator.pop(context);
},
),
)
],
);

十二、 Card(圆角和阴影卡片)

1
2
3
4
5
6
7
 Card(
elevation: 0.8,
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(6.0)),
),
)

十三、 TextField(输入框)

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
  final TextEditingController controller = TextEditingController();

@override
void initState() {
super.initState();
controller.addListener(() {
print('你输入的内容为${controller.text}');
});
}


TextField(
controller: controller,
maxLength: 30,
maxLines: 1,
autofocus: true,
obscureText: false,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 26.0, color: Colors.green),
onChanged: (text) {
print('文本内容改变时回调$text');
},
enabled: true,
decoration: InputDecoration(
fillColor: Colors.grey.shade200,
filled: true,
helperText: '用户名',
prefixIcon: Icon(Icons.person),
suffixText: '用户名',
),
);

十四、 Clip裁剪

ClipOval圆形裁剪

1
2
3
4
5
6
7
ClipOval(
child: SizedBox(
width: 100.0,
height:100.0,
child: ...
),
),

ClipRRect圆角矩形裁剪

1
2
3
4
5
6
7
ClipRRect(
borderRadius: BorderRadius.all(new Radius.circular(10.0)),
child: new SizedBox(
width: 100.0,
height:100.0,
child: ...
)

ClipRect矩形裁剪
ClipPath路径裁剪(可实现不规则背景)
路径裁剪需要自己实现clipper

1
2
3
4
5
6
7
ClipPath(
clipper: _StarCliper(radius: 50.0),
child:new SizedBox(
width: 100.0,
height:100.0,
child: ...
)
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
class _StarCliper extends CustomClipper<Path>{

final double radius;

_StarCliper({this.radius});

/// 角度转弧度公式
double degree2Radian(int degree) {
return (Math.pi * degree / 180);
}

@override
Path getClip(Size size) {
double radius = this.radius;
Path path = new Path();
double radian = degree2Radian(36);// 36为五角星的角度
double radius_in = (radius * Math.sin(radian / 2) / Math
.cos(radian)); // 中间五边形的半径

path.moveTo((radius * Math.cos(radian / 2)), 0.0);// 此点为多边形的起点
path.lineTo((radius * Math.cos(radian / 2) + radius_in
* Math.sin(radian)),
(radius - radius * Math.sin(radian / 2)));
path.lineTo((radius * Math.cos(radian / 2) * 2),
(radius - radius * Math.sin(radian / 2)));
path.lineTo((radius * Math.cos(radian / 2) + radius_in
* Math.cos(radian / 2)),
(radius + radius_in * Math.sin(radian / 2)));
path.lineTo(
(radius * Math.cos(radian / 2) + radius
* Math.sin(radian)), (radius + radius
* Math.cos(radian)));
path.lineTo((radius * Math.cos(radian / 2)),
(radius + radius_in));
path.lineTo(
(radius * Math.cos(radian / 2) - radius
* Math.sin(radian)), (radius + radius
* Math.cos(radian)));
path.lineTo((radius * Math.cos(radian / 2) - radius_in
* Math.cos(radian / 2)),
(radius + radius_in * Math.sin(radian / 2)));
path.lineTo(0.0, (radius - radius * Math.sin(radian / 2)));
path.lineTo((radius * Math.cos(radian / 2) - radius_in
* Math.sin(radian)),
(radius - radius * Math.sin(radian / 2)));

path.close();// 使这些点构成封闭的多边形

return path;
}

@override
bool shouldReclip(_StarCliper oldClipper) {
return this.radius != oldClipper.radius;
}

}

十五、 Baseline(基准线)

十六、 Offstage(是否显示)

1
2
3
4
5
6
7
8
9
op bool offstage = true;
Offstage(
offstage: offstage,
child: Text(
'我出来啦!',
style: TextStyle(fontSize: 36.0),
),
),
);

十七、 FittedBox(自动缩放)

1
2
3
4
5
6
7
8
9
10
11
//当child的Text很长时,会自动被缩小到很小。也提供fit属性。
FittedBox(
child: Text(
"我是单条rss的标题我是单条rss的标题我是单条rss的标题我是单条rss的标题我是单条rss的标题",
textAlign: TextAlign.left,
softWrap: true,
style: TextStyle(
color: Theme.of(context).primaryColor,
fontSize: 16.0),
),
),

十八、Flex(弹性布局)

类似Row或Column。Row和Column为其子控件。
vertical: 子child排列方向
其他属性含义可以参考Row或Column
mainAxisSize:主轴尺寸

1
2
3
4
5
6
7
Flex(
crossAxisAlignment: CrossAxisAlignment.start,
direction: Axis.vertical,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[],
)

十九、RefreshIndicator(下拉刷新)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey = GlobalKey<
RefreshIndicatorState>();
RefreshIndicator(
key: _refreshIndicatorKey,
onRefresh: _refresh,
),
@override
void initState() {
super.initState();
//进入后自动刷新
WidgetsBinding.instance.addPostFrameCallback((_){
_refreshIndicatorKey.currentState.show ();
});
}
//onRefresh的方法,要有async和await,没有await,刷新会立刻结束,没有async,刷新图标不会出现
Future<Null> _refresh() async{
//await
}

二十、Dismissible(侧滑删除)

可用于ListView。

1
2
3
4
5
6
7
8
9
10
11
12
13
Dismissible(
key: Key(data.id.toString()),
background: Container(
color: Colors.red,
),
direction: DismissDirection.endToStart,
confirmDismiss: _confirmDismiss,
child:
),

Future<bool> _confirmDismiss(DismissDirection direction)async{
return false;
}