使用flutter寫app實(shí)戰(zhàn)已告一段落妻怎,從剛接觸概念不清道現(xiàn)在能使用組件寫頁(yè)面,踩了不少坑泞歉,這里記錄一下逼侦,沒(méi)有順序可言,覺(jué)得值得記錄就寫下來(lái)
寫在前面:
- 設(shè)備要ok腰耙,我在win7上就是浪費(fèi)了不少時(shí)間榛丢。前文安裝環(huán)境都踩了很多不要必要坑,卡的要死挺庞,webview還莫名閃退晰赞。升級(jí)硬件軟件后真的很絲滑
- 老老實(shí)實(shí)用andirod studio里面的avd,別用那些夜神什的么选侨。再用vs code開(kāi)發(fā)掖鱼。別問(wèn)為什么,反正就是:潤(rùn)
正文
- 剛接觸flutter 援制,你肯定要理解動(dòng)態(tài)組件StatefulWidget 和靜態(tài)組件StatelessWidget锨用。我覺(jué)得只要記得一點(diǎn)就可以:
StatefulWidget 可以用setState實(shí)現(xiàn)重新build更新視圖,StatelessWidget不行隘谣,完全依賴父組件的變化而變化。
當(dāng)你看到重新build更新視圖這句話,你肯定會(huì)跟我一樣想寻歧,每次setState都重新build掌栅,所有的子組件也重新build了,十分不劃算码泛。
我是這么認(rèn)為的:flutter借鑒了react猾封,所以也有自己的diff算法,只更新修改部分噪珊。如果你想給diff的過(guò)程減少一些遍歷或者加快遍歷晌缘。可以:
第一: 靜態(tài)元素前面加const 痢站,例如: const Text('我是靜態(tài)文字')
第二:各種局部更新:FutureBuilder磷箕,StreamBuilder,GlobalKey
第三:合理封裝使用一些StatelessWidget
-
承接第一點(diǎn)阵难,你使用的組件分動(dòng)態(tài)和靜態(tài)岳枷,在靜態(tài)組件中想用setState是不行的:
在SimpleDialog()中的子組件默認(rèn)是無(wú)狀態(tài)的,你想有按鈕要切換狀態(tài)要在外層包裹StatefulBuilder組件
Widget category(context, k) {
return InkWell(
child: Wrap(children: [
Text(k['categoryName'] ?? '商品類別',
style: TextStyle(color: Color(0xff999999), fontSize: 12)),
Icon(
Icons.chevron_right,
size: 18,
color: Color(0xff999999),
)
]),
onTap: () {
bool hasClick = false;
showDialog(
context: context,
builder: (context) {
// 不包裹它, 里面的點(diǎn)擊setState改變狀態(tài)不生效
return StatefulBuilder(builder: (BuildContext context,
void Function(void Function()) setState) {
return SimpleDialog(
children: <Widget>[
SingleChildScrollView(
child: Column(
children: k['childCategoriesList'].map<Widget>((item) {
return CheckboxListTile(
title: Text(item['categoryName']),
value: k['selChildCateIdList']
.indexOf(int.parse(item['categoryId'])) >
-1,
onChanged: (value) {
hasClick = true;
if (value) {
setState(() {
k['selChildCateIdList']
.add(int.parse(item['categoryId']));
});
} else {
setState(() {
k['selChildCateIdList']
.remove(int.parse(item['categoryId']));
});
}
},
);
}).toList()),
)
],
);
});
}).then((value) {
// 點(diǎn)擊蒙層關(guān)閉彈窗這里就執(zhí)行
if (hasClick) {
String ids = k['selChildCateIdList'].join(',');
GoodsService.updateCategory(
{'goodsId': k['goodsId'], 'categroyIds': ids}, context)
.then((result) {
showToast(message: '類別修改成功');
});
}
});
},
);
}
- 以上代碼循環(huán)列表產(chǎn)生一組數(shù)據(jù)和按鈕呜叫,使用到了SingleChildScrollView空繁,它沒(méi)有“懶加載”模式。超過(guò)一屏的組件有請(qǐng)求朱庆,在ListView中是滾動(dòng)到視圖以后才請(qǐng)求盛泡。具體情況可以自行感受
4.既然提到了ListView,再提一點(diǎn):
平常我們會(huì)使用Expanded來(lái)利用剩余空間(Expanded組件必須用在Row娱颊、Column傲诵、Flex內(nèi)),
IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.only(right: 5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
),
clipBehavior: Clip.antiAlias,
child: Image.network(
'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1600779969615&di=79f315fcf295c09b25b09466bcb97257&imgtype=0&src=http%3A%2F%2Ft8.baidu.com%2Fit%2Fu%3D1484500186%2C1503043093%26fm%3D79%26app%3D86%26f%3DJPEG%3Fw%3D1280%26h%3D853',
height: 75,
width: 75,
fit: BoxFit.cover,
),
),
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Expanded(
child: Container(
width: 230,
child: Text(
k['goodsName'],
overflow: TextOverflow.ellipsis,
maxLines: 2,
),
)),
Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
category(context, k),
k['activityBeginDate'] == null ||
k['activityEndDate'] == null
? SizedBox(height: 0)
: Padding(
padding: EdgeInsets.only(top: 3),
child: RichText(
text: TextSpan(
style:
DefaultTextStyle.of(context).style,
children: <InlineSpan>[
TextSpan(
text:
'${k['activityBeginDate'].split(' ')[0]}—',
style: TextStyle(
color: Color(0xff999999),
fontSize: 12)),
TextSpan(
text: k['activityEndDate']
.split(' ')[0],
style: TextStyle(
color: Color(0xff999999),
fontSize: 12))
])))
]))
]),
],
)),
- 既然提到了Expanded
如果遇到這樣的報(bào)錯(cuò):The method '>' was called on null 然后報(bào)錯(cuò)里面有row或者column颅痊,界面上超出等現(xiàn)象殖熟,一定要注意看是否合理使用了Expanded包裹可能超出界限的元素
- Color(0xffeeeeee)
Color(int value)
Color(0xFF3CAAFA),value接收的是一個(gè)十六進(jìn)制(0x開(kāi)頭),FF表示的是十六進(jìn)制透明度(00-FF),3CAAFA是十六進(jìn)制色值。
Color.fromRGBO(int r, int g, int b, double opacity)
Color.fromRGBO(60, 170, 250, 1)斑响,r菱属、g、b分別表示red舰罚、green纽门、blue,常規(guī)的紅綠藍(lán)三色营罢,取值范圍為0-255赏陵,opacity表示透明度饼齿,取值0.0-1.0。
Color.fromARGB(int a, int r, int g, int b)
Color.fromARGB(255, 60, 170, 250),a表示透明度蝙搔,取值0-255缕溉,rgb同上一樣。
Colors._()
Colors類定義了很多顏色吃型,可以直接使用证鸥,例如 Colors.blue,其實(shí)就是第一種Color(int value)的封裝。
7.InkWell和GestureDetector
前者有水波紋和簡(jiǎn)單的事件勤晚,后者有更多事件枉层,一般InkWell夠用了
8.Offstage 和visibility ( Opacity 可以實(shí)現(xiàn)蒙層,AnimatedOpacity動(dòng)畫)
前者簡(jiǎn)單的隱藏和顯示赐写。 后者能控制元素是否在內(nèi)存中等鸟蜡。最簡(jiǎn)單實(shí)現(xiàn)顯示隱藏的自然是Opacity。
Offstage 的參數(shù)要注意:當(dāng)offstage為true血淌,當(dāng)前控件不會(huì)被繪制在屏幕上矩欠,不會(huì)響應(yīng)點(diǎn)擊事件,也不會(huì)占用空間悠夯,當(dāng)offstage為false癌淮,當(dāng)前控件則跟平常用的控件一樣渲染繪制
9.Padding設(shè)置邊距,SizeBox更好用
- 在vs code裝了flutter dart擴(kuò)展后沦补,快捷鍵生成組件:
stful 會(huì)自動(dòng)生成相應(yīng)最簡(jiǎn)單的想要代碼
stanim 會(huì)生成帶有生命周期的代碼
stle 自動(dòng)生成StatelessWidget
注: 用快捷鍵生成的state有下劃線屬于私有乳蓄,父組件訪問(wèn)不到,使用globalkey更新子組件數(shù)據(jù)時(shí)候要注意
final GlobalKey<StaticTableState> _tableKey = GlobalKey<StaticTableState>();
- 常見(jiàn)場(chǎng)景:表格的數(shù)據(jù)都是double夕膀,獲取到以后展示在Text中會(huì)有25.0虚倒,不像js在瀏覽器中拿到數(shù)據(jù)的時(shí)候,數(shù)字就已經(jīng)去掉多余后綴了产舞。我這里替換掉魂奥,不知道還有啥好辦法(切割對(duì)比就不說(shuō)了)
data[i][e['key']] .toString() .replaceAll(RegExp(r'.0$'), '');
12.使用SliverAppBar代替AppBar, 使用ListTile代替Row+Container,使用RichText代替Text(一段文字多色)
flutter packages get //獲取pubspec.yaml文件中列出的所有依賴包
flutter packages upgrade //獲取pubspec.yaml文件中列出的所有依賴包的最新版本
圖標(biāo)庫(kù),圖表太多可以看這里去選擇易猫,也可以使用自己的圖標(biāo)https://material.io/resources/icons/?icon=more_horiz&style=baseline
-
Flutter打包release版本安卓apk包真機(jī)安裝無(wú)法請(qǐng)求網(wǎng)絡(luò)的解決方法
- 常見(jiàn)錯(cuò)誤:type 'List<dynamic>'不是'List<Widget>'類型的子類型
- 由于radio組件無(wú)法更改間距什么的耻煤,使用ChoiceChip自定義radio,循環(huán)的單選都可以用ChoiceChip實(shí)現(xiàn)准颓。當(dāng)然完全自己畫自定義也是可以的哈蝇,使用setState更新?tīng)顟B(tài) 參考文章
class MyRadio extends StatelessWidget {
final int index;
final String label;
final parent;
MyRadio(
{Key key,
@required this.index,
@required this.parent,
@required this.label})
: super(key: key);
@override
Widget build(BuildContext context) {
return ChoiceChip(
avatar: Stack(alignment: Alignment.center, children: [
Container(
width: 15,
height: 15,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100),
color: parent.selected == index ? Colors.blue : Colors.white,
border: Border.all(
color: parent.selected == index
? Colors.blue
: Color(0xffcccccc),
width: 1,
))),
Container(
width: 7,
height: 7,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100),
color: Colors.white,
border: Border.all(
color: Colors.white,
width: 1,
)))
]),
label: Text(label),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
//設(shè)置為MaterialTapTargetSize.shrinkWrap時(shí)
//,clip距頂部距離為0攘已;設(shè)置為MaterialTapTarget
//Size.padded時(shí)距頂部有一個(gè)距離
labelPadding: EdgeInsets.all(0),
labelStyle: TextStyle(fontSize: 12, color: Color(0xff999999)),
padding: EdgeInsets.all(0),
selected: parent.selected == index,
selectedColor: Colors.white,
backgroundColor: Colors.white,
selectedShadowColor: Colors.white,
elevation: 0,
pressElevation: 0,
onSelected: (v) {
if (v && parent.selected != index) {
parent.onSelectedChanged(index);
}
},
);
}
}
在父組件使用要定義一個(gè)selected 初始值炮赦,和onSelectedChanged方法,
int selected = 0; // 標(biāo)識(shí)選中的radio
Container(
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.all(20),
color: Colors.white,
child: Wrap(
spacing: 10,
children: channelList
.asMap()
.keys
.map((i) =>
MyRadio(index: i, label: channelList[i], parent: this))
.toList(),
))
- flutter TextField垂直居中,去掉默認(rèn)的padding
decoration: InputDecoration(
contentPadding: EdgeInsets.all(0),)
- 我們一般設(shè)置padding样勃,margin會(huì)使用EdgeInsets
例如上面代碼中的:
padding: EdgeInsets.all(20), // 四周邊距
padding:EdgeInsets.symmetric(horizontal: 3, vertical: 5) // 左右吠勘, 上下
padding:EdgeInsets.fromLTRB(left, top, right, bottom) // 顯而易見(jiàn)
那么給文本設(shè)置呢性芬?
EdgeInsetsDirectional EdgeInsetsGeometry可以用在文本上
titlePadding: const EdgeInsetsDirectional.only(start: 16.0, bottom: 14.0)
在實(shí)操中當(dāng)然不止這些問(wèn)題,還有動(dòng)畫看幼,緩存等等批旺,內(nèi)容多如牛毛,一步三百度是常態(tài)诵姜,暫時(shí)記錄這些。
flutter版本更新很快搏熄,每個(gè)版本組件的屬性變化都比較大棚唆,插件也會(huì)跟新版更新,如果你也在了解flutter心例,祝你好運(yùn)