flutter階段雜記

使用flutter寫app實(shí)戰(zhàn)已告一段落妻怎,從剛接觸概念不清道現(xiàn)在能使用組件寫頁(yè)面,踩了不少坑泞歉,這里記錄一下逼侦,沒(méi)有順序可言,覺(jué)得值得記錄就寫下來(lái)
寫在前面:

  1. 設(shè)備要ok腰耙,我在win7上就是浪費(fèi)了不少時(shí)間榛丢。前文安裝環(huán)境都踩了很多不要必要坑,卡的要死挺庞,webview還莫名閃退晰赞。升級(jí)硬件軟件后真的很絲滑
  2. 老老實(shí)實(shí)用andirod studio里面的avd,別用那些夜神什的么选侨。再用vs code開(kāi)發(fā)掖鱼。別問(wèn)為什么,反正就是:潤(rùn)

正文

  1. 剛接觸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

  1. 承接第一點(diǎn)阵难,你使用的組件分動(dòng)態(tài)和靜態(tài)岳枷,在靜態(tài)組件中想用setState是不行的:
    在SimpleDialog()中的子組件默認(rèn)是無(wú)狀態(tài)的,你想有按鈕要切換狀態(tài)要在外層包裹StatefulBuilder組件


    企業(yè)微信截圖_16050666358073.png
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: '類別修改成功');
          });
        }
      });
    },
  );
}
  1. 以上代碼循環(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)),

但是在listview中子項(xiàng)cloum中使用expend報(bào)錯(cuò),因?yàn)楦叨炔淮_定维蒙,可以使用IntrinsicWidth/IntrinsicHeight掰吕,只要column中有一個(gè)有高度(例如左側(cè)圖片),其他會(huì)自動(dòng)保持一樣高
企業(yè)微信截圖_16050669844716.png
 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))
                                    ])))
                      ]))
                ]),
              ],
            )),
  1. 既然提到了Expanded
    如果遇到這樣的報(bào)錯(cuò):The method '>' was called on null 然后報(bào)錯(cuò)里面有row或者column颅痊,界面上超出等現(xiàn)象殖熟,一定要注意看是否合理使用了Expanded包裹可能超出界限的元素
  1. 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更好用

  1. 在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>();
  1. 常見(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文件中列出的所有依賴包的最新版本

  1. 圖標(biāo)庫(kù),圖表太多可以看這里去選擇易猫,也可以使用自己的圖標(biāo)https://material.io/resources/icons/?icon=more_horiz&style=baseline

  2. Flutter打包release版本安卓apk包真機(jī)安裝無(wú)法請(qǐng)求網(wǎng)絡(luò)的解決方法

  1. 常見(jiàn)錯(cuò)誤:type 'List<dynamic>'不是'List<Widget>'類型的子類型
  1. 由于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方法,

使用Wrap來(lái)渲染一組自定義radio
企業(yè)微信截圖_16050759754958.png
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(),
              ))
  1. flutter TextField垂直居中,去掉默認(rèn)的padding

decoration: InputDecoration(

contentPadding: EdgeInsets.all(0),)

  1. 我們一般設(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)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宵凌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子止后,更是在濱河造成了極大的恐慌瞎惫,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件译株,死亡現(xiàn)場(chǎng)離奇詭異瓜喇,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)歉糜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門乘寒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人匪补,你說(shuō)我怎么就攤上這事伞辛。” “怎么了夯缺?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵蚤氏,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我踊兜,道長(zhǎng)竿滨,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任润文,我火速辦了婚禮姐呐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘典蝌。我一直安慰自己曙砂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布骏掀。 她就那樣靜靜地躺著鸠澈,像睡著了一般柱告。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上笑陈,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天际度,我揣著相機(jī)與錄音,去河邊找鬼涵妥。 笑死乖菱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蓬网。 我是一名探鬼主播窒所,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼帆锋!你這毒婦竟也來(lái)了吵取?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤锯厢,失蹤者是張志新(化名)和其女友劉穎皮官,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體实辑,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡捺氢,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了徙菠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片讯沈。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情尖淘,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布挤茄,位于F島的核電站,受9級(jí)特大地震影響冰木,放射性物質(zhì)發(fā)生泄漏穷劈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一踊沸、第九天 我趴在偏房一處隱蔽的房頂上張望歇终。 院中可真熱鬧,春花似錦逼龟、人聲如沸评凝。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)奕短。三九已至宜肉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間翎碑,已是汗流浹背谬返。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留日杈,地道東北人遣铝。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像达椰,于是被迫代替她去往敵國(guó)和親翰蠢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容