前言
拖了好久终于开始看Flutter,为了加深印象巩固学习效果,现将每次学习的内容总结成笔记,巩固效果也方便以后查阅。同时,笔记相关的Demo项目已经上传到Github,这里是传送门,欢迎指正。
正文
Flutter相关文档
1.官方文档
2.Flutter中文网
3.官方Demo(Github地址)
基础控件目录
官方基础控件目录
常用基础控件
不管在哪个前端平台,常用的基础控件都一样,大致包括:文本、图片、按钮、输入框、进度条、单选框和复选框、开关切换(Switch)、提示窗、对话框;布局用的控件(Layout)基本包含:横向布局、纵向布局、格子布局、重叠布局、各类包含不同约束条件的特定布局(内外间距,靠边居中,相对位置等);然后是一些根据常用程度封装的控件包括:列表控件、滑动控件(横纵方向),下拉刷新,抽屉(Drawer)等。
然后在Flutter中,控件分为StatefulWidget和StatelessWidget(有状态控件和无状态控件),无状态控件是死的,不会动态更新,所以实际使用的时候大多数情况下都是封装自己的StatefulWidget,StatefulWidget的状态存储在对应的State类里面,需要更新的时候可以通过调用setState()方法来刷新页面。
StatefulWidget基本代码模板如下:
class DemoStatefulWidget extends StatefulWidget{
@override
State createState() {
return DemoState();
}
}
class DemoState extends State{
var data;
@override
Widget build(BuildContext context) {
return Text(data);
}
void onDataChanged(newData){
setState(() {//刷新控件
data = newData;
});
}
}
接下来,按照上面梳理出的常用控件,我们一个一个来看。
文本 Text
Text官方文档
没啥注意点,属性看文档基本都能看懂。
基本使用
Widget build(BuildContext context) {
return new Text(
"A text in container",
style: new TextStyle(
color: Color(0xFF0000CD), fontStyle: FontStyle.normal, fontSize: 15),
);
}
图片 Image
Image官方文档
也没啥注意点。
基本使用
Widget build(BuildContext context) {
return Image.network(//图片来自网络
imageUrl,
width: 200.0,
height: 150.0,
);
// Image.asset(name);//图片来自项目内的资源
// Image.file(file);//图片来自文件
// Image.memory(bytes);//图片来自内存
}
按钮 RaisedButton FlatButton
这两个按钮除了风格不同以外没什么别的区别,只有个别属性彼此有区分。
RaisedButton官方文档
FlatButton官方文档
Widget build(BuildContext context) {
return RaisedButton(
elevation: 5,
color: Color(0xff87CEFA),
child: Text("Print"),
onPressed: () {}
);
}
输入框 TextField
TextField官方文档
基本使用
Widget build(BuildContext context) {
return TextField(
maxLines: 5,
style: TextStyle(color: Colors.black),
controller: editController,
obscureText: false,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Text',
)
);
进度条 Slider
Slider官方文档
注意点:
dividions属性,比如设置dividions属性为4,进度条被分为四等分,滑动时进度会停留在等分点上;
label属性,设置之后,滑动时,标签将展示在进度条上;
效果如下:
代码:
Widget build(BuildContext context) {
return Slider(
label: 'slider',
value: rating,
divisions: 4,
onChanged: (newValue) {
setState(() {
rating = newValue;
});
print('rating: ${rating}'); //打日志到控制台)
},
);
CheckBox
很简单没啥要注意的。
CheckBox官方文档
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.fromLTRB(0.0, 10, 0, 10),
margin: EdgeInsets.fromLTRB(0, 10, 15, 10),
alignment: Alignment(-1, 0),
//min和max为最小进度值和最大进度值,不设置的话默认为0到1
child: Slider(
inactiveColor: Colors.cyan[100],
activeColor: Colors.cyan[400],
min: 0,
max: 2,
label: 'slider',
value: rating,
onChanged: (newValue) {
setState(() {
rating = newValue;
});
print('rating1: ${rating}'); //打日志到控制台)
},
),
);
}
Radio
Radio官方文档
这个看文档有点迷糊,而且这个使用和Android的不一样。
这里要注意的是value和groupValue两个属性,value是当这个Radio选中的时候更新到onChange方法里的值,groupValue是,当它的值和value的值相同时,这个Radio就是选中状态,否则就是非选中状态。所以一般使用时都是把groupValue的值设定为一个动态变量,然后在onChange方法里更新这个动态变量。
基本使用
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
//靠左
verticalDirection: VerticalDirection.down,
children: [
Container(
margin: EdgeInsets.fromLTRB(0, 10, 15, 0),
child:
Text('Radio:')),
Row(
children: [
Text('1'),
Radio(
value: '1',
groupValue: this.radioSelectedPosition,
onChanged: (radioChange) {
setState(() {
this.radioSelectedPosition = radioChange;
});
print('radioChange1: ${radioChange}'); //打日志到控制台)
},
),
],
),
Row(
children: [
Text('2'),
Radio(
value:'2',
groupValue: this.radioSelectedPosition,
onChanged: (radioChange) {
setState(() {
this.radioSelectedPosition = radioChange;
});
print('radioChange2: ${radioChange}'); //打日志到控制台)
},
),
],
),
Row(
children: [
Text('3'),
Radio(
value: '3',
groupValue: this.radioSelectedPosition,
onChanged: (radioChange) {
setState(() {
this.radioSelectedPosition = radioChange;
});
print('radioChange3: ${radioChange}'); //打日志到控制台)
},
),
],
),
]);
}
效果:
Switch
Switch官方文档
Widget build(BuildContext context) {
return Switch(
activeColor: Colors.deepOrange[300],
activeTrackColor: Colors.deepOrange[600],
inactiveTrackColor: Colors.blue[300],
inactiveThumbColor: Colors.blue[100],
inactiveThumbImage: NetworkImage(imageUrl),
value: switchOn,
onChanged: (switchOn){
setState(() {
this.switchOn = switchOn;
print('switchOn: ${switchOn}'); //打日志到控制台)
});
},
);
}
最后,有一个重要而且特别的属性
在看第一个控件的时候,我就注意到这个属性,key,排在第一位。简单看文档看不明白到底是做什么用的,然后往下看的时候我注意到每一个控件都有key这个属性,而且都是排在第一位。于是我去查看了官方文档,结果我的个乖乖,开头就是一个整整10分钟的讲解视频,由于视频是全英文的并且字幕翻译的并不好,我反复看了三遍,才大致摸清楚了key的含义和用法。
要理解这个key的使用必须要对Flutter的控件树刷新机制有简单的了解。Flutter在构建Widget树的时候,同时会构建一个Element树,这个Element树才是显示和刷新页面的时候真正起作用的东西,Widget里面只是存了一些控件的属性值而已。在刷新Element树的时候,Flutter会依次对比每个控件的改变状态,首先是检查type,如果两个控件是相同的类型,type就是一样的,然后是检查key,比如你交换了页面中两个Text的位置,改变了Widget树的结构,如果此时你对这个控件设置了key,它会检查出前后状态下控件的key不一样,会判定这里有修改,然后Flutter会去遍历和刷新Element树,将它调整到正确的交换后的状态,否则的话,由于两个Text的type是一样的,Flutter检查之后会认为Element树没有必要刷新,此时就会出现错误,你会发现你交换控件之后页面上两个文本没有丝毫改变,如果你不了解这个机制的话,这个错误会显得莫名其妙并且完全摸不着头脑。
然后,key还包括ValueKey、LocalKey、GlobleKey,各种场景下适合使用的类型不相同,详细可见官方文档。然后后来找到一篇文章,基本很详细的把官方视频要阐述的内容解释清楚了,好文传送门。
尾声
以上,第一部分暂时整理这么多,接下来空余时间再逐步梳理剩下的控件,预计整个常用控件的梳理大概要分3篇笔记。个人从零逐步学习Flutter,水平有限,如发现问题,欢迎斧正!