Flutter學習筆記5-StatelessWidget缺前、StatefulWidget

本篇主要對StatelessWidget、StatefulWidget做一個簡單的介紹井辜,并實際運用來進行一些簡單界面的構(gòu)建

一、StatelessWidget

StatelessWidget子類代碼塊生成快捷鍵: stless
上篇已經(jīng)簡單介紹了StatelessWidget的基本使用管闷,接下來簡單介紹如何使用StatelessWidget創(chuàng)建一個商品列表粥脚,效果如下:


Simulator Screen Shot - iPhone 12 - 2021-08-22 at 10.20.36.png
  1. StatelessWidget參數(shù)傳遞
    與Dart語法一致,創(chuàng)建屬性(final修飾)包个,實現(xiàn)初始化方法刷允,進行賦值
    class YWHomeProduct extends StatelessWidget {
      final String title;
      final String desc;
      final String imageUrl;
    
      YWHomeProduct(this.title, this.desc, this.imageUrl);
    
      @override
      Widget build(BuildContext context) {
        return Column(children: [Text(title), Text(desc), Image.network(imageUrl)]);
      }
    }
    
    調(diào)用:
    YWHomeProduct("瓜子", "焦糖瓜子,香甜可口",
          "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.yzcdn.cn%2Fupload_files%2F2018%2F08%2F21%2FFiLdNHrxKMohbPJ2QbtOTo9MhIMm.jpg%3FimageView2%2F2%2Fw%2F580%2Fh%2F580%2Fq%2F75%2Fformat%2Fjpg&refer=http%3A%2F%2Fimg.yzcdn.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1632121924&t=b37df3a44a89fc3b768c0f3f69f94f2a")
    
  2. 豎向列表
  • column: 豎向布局多個控件碧囊,使用column布局树灶,當豎向控件高度超出屏幕高度后,會顯示條紋遮擋
  • ListView: 豎向可滾動控件
  1. 對象創(chuàng)建:
    函數(shù)內(nèi)部可以創(chuàng)建局部變量呕臂,減小語句內(nèi)部代碼量破托,但是每次調(diào)用函數(shù)都會創(chuàng)建
    在類的內(nèi)部創(chuàng)建變量,則只會在對象實例化時創(chuàng)建一次
    在類的外部創(chuàng)建變量歧蒋,則在整個項目生命周期中都會存在

  2. 創(chuàng)建控件之間的邊距
    可以通過SizedBox控件來實現(xiàn)土砂,橫向距離width,豎向距離height

    SizedBox(height: 8)
    
  3. 邊框創(chuàng)建
    將某個控件放置到Container中谜洽,可以用Container來設(shè)置邊框(decoration)以及內(nèi)邊距(padding)等

    return Container(
        padding: EdgeInsets.all(8),
        decoration:
            BoxDecoration(border: Border.all(color: Colors.purple, width: 8)),
        child: Column(children: [
          Text(title, style: titleStyle),
          SizedBox(height: 8),
          Text(desc, style: descStyle),
          SizedBox(height: 8),
          Image.network(imageUrl)
        ]));
    

    問題:BoxDecoration的主要用處萝映?
    用于Widget的裝飾

    color         顏色背景         Color類型
    image         圖片背景         DecorationImage類型
    border        邊界            BoxBorder類型
    borderRadius  圓角邊界半徑     BorderRadiusGeometry類型
    boxShadow     陰影            List<BoxShadow>類型
    gradient      漸變色          Gradient類型
    backgroundBlendMode 背景混合模式    BlendMode類型
    shape         形狀            BoxShape類型
    
  4. 控制column豎向布局控件位置
    控件分為主軸(豎向)和交叉軸(橫向)
    主軸使用MainAxisAlignment控制子控件位置
    交叉軸使用CrossAxisAlignment控制子控件位置
    通過Flex可以決定主軸和交叉軸

    Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
          Text(title, style: titleStyle),
          SizedBox(height: 8),
          Text(desc, style: descStyle),
          SizedBox(height: 8),
          Image.network(imageUrl)
        ])
    

下面是完整的代碼:

import 'package:flutter/material.dart';

main(List<String> args) {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(debugShowCheckedModeBanner: false, home: YWHomePage());
  }
}

class YWHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(appBar: AppBar(title: Text("購物車")), body: YWBodyContent());
  }
}

class YWBodyContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListView(children: [
      YWHomeProduct("瓜子", "焦糖瓜子,香甜可口",
          "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.yzcdn.cn%2Fupload_files%2F2018%2F08%2F21%2FFiLdNHrxKMohbPJ2QbtOTo9MhIMm.jpg%3FimageView2%2F2%2Fw%2F580%2Fh%2F580%2Fq%2F75%2Fformat%2Fjpg&refer=http%3A%2F%2Fimg.yzcdn.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1632121924&t=b37df3a44a89fc3b768c0f3f69f94f2a"),
      YWHomeProduct("花生", "鹽水花生阐虚,補氣養(yǎng)血",
          "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fi2.chuimg.com%2F25d9caf08b6811e6a9a10242ac110002_1171w_801h.jpg%3FimageView2%2F2%2Fw%2F660%2Finterlace%2F1%2Fq%2F90&refer=http%3A%2F%2Fi2.chuimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1632122384&t=f3859622328496babe08d62ffca276ba"),
      YWHomeProduct("八寶粥", "居家旅行序臂,出門良品",
          "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fcp2.douguo.net%2Fupload%2Fdish%2F7%2F3%2F3%2F600_7355c671e3bf52100c61e043c6110ae3.jpg&refer=http%3A%2F%2Fcp2.douguo.net&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1632122038&t=cedcc17ba9ed999844c9c3c1bedf2ded")
    ]);
  }
}

class YWHomeProduct extends StatelessWidget {
  final String title;
  final String desc;
  final String imageUrl;
  final titleStyle = TextStyle(fontSize: 20, color: Colors.orange);
  final descStyle = TextStyle(fontSize: 15, color: Colors.blue);

  YWHomeProduct(this.title, this.desc, this.imageUrl);

  @override
  Widget build(BuildContext context) {
    return Container(
        padding: EdgeInsets.all(8),
        decoration:
            BoxDecoration(border: Border.all(color: Colors.purple, width: 8)),
        child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
          Text(title, style: titleStyle),
          SizedBox(height: 8),
          Text(desc, style: descStyle),
          SizedBox(height: 8),
          Image.network(imageUrl)
        ]));
  }
}

二、StatefulWidget

  1. StatefulWidget用法
    有狀態(tài)需要改變的時候要使用StatefulWidget
  • StatefulWidget內(nèi)部有一個抽象方法createState实束,此方法返回一個State類奥秆,繼承自StatefulWidget的類需要實現(xiàn)此方法
  • 在自定義的State子類中需要實現(xiàn)build方法生成一個控件
  • StatefulWidget內(nèi)部不可直接定義狀態(tài),但是可以在自定義的State子類中可以定義狀態(tài)

問題:為什么Flutter在設(shè)計的時候咸灿,StatefulWidget的build方法放在State中构订?

  • build出來的widget是需要依賴State中的變量(狀態(tài)/數(shù)據(jù))
  • 在Flutter的運行過程中,widget是不斷銷毀和創(chuàng)建的避矢,當我們的狀態(tài)發(fā)生改變時悼瘾,并不希望重新創(chuàng)建一個新的State
  1. 使用StatefulWidget構(gòu)建一個計數(shù)器

    效果圖如下:
    Simulator Screen Shot - iPhone 12 - 2021-08-21 at 20.08.41.png

    其中按鈕可以使用ElevatedButton(RaisedButton已廢棄)

    使用Column構(gòu)建豎向組件,Row構(gòu)建橫向組件

import 'package:flutter/material.dart';

main(List<String> args) {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(debugShowCheckedModeBanner: false, home: YWHomePage());
  }
}

class YWHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(appBar: AppBar(title: Text("購物車")), body: YWHomeContent());
  }
}

class YWHomeContent extends StatefulWidget {
  @override
  _YWHomeContentState createState() => _YWHomeContentState();
}

class _YWHomeContentState extends State<YWHomeContent> {
  int number = 0;
  @override
  Widget build(BuildContext context) {
    return Center(
        child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
      getButtons(),
      Text("當前計數(shù):$number", style: TextStyle(fontSize: 20))
    ]));
  }

  Widget getButtons() {
    return Row(mainAxisAlignment: MainAxisAlignment.center, children: [
      ElevatedButton(
          child: Text("+", style: TextStyle(fontSize: 20)),
          style: ButtonStyle(
              backgroundColor: MaterialStateProperty.all(Colors.pink)),
          onPressed: () {
            setState(() {
              number++;
            });
          }),
      ElevatedButton(
          child: Text("-", style: TextStyle(fontSize: 20)),
          style: ButtonStyle(
              backgroundColor: MaterialStateProperty.all(Colors.purple)),
          onPressed: () {
            setState(() {
              if (number > 0) {
                number--;
              }
            });
          })
    ]);
  }
}

問題:數(shù)值改變能放在setState外面嗎审胸?

setState會觸發(fā)widget build方法重新進行渲染

  1. StatefulWidget傳值
    父類給子類傳值亥宿,傳值方式與StatelessWdight語法一致,創(chuàng)建屬性(final修飾)砂沛,實現(xiàn)對應(yīng)初始化方法烫扼,進行賦值,將參數(shù)傳遞給Widget子類
    然后在State類中通過this.widget來實現(xiàn)取值
    class YWHomeContent extends StatefulWidget {
      final String message;
      YWHomeContent(this.message);
      @override
      _YWHomeContentState createState() =>_YWHomeContentState();
    }
    
    class _YWHomeContentState extends State<YWHomeContent> {
      int number = 0;
      @override
      Widget build(BuildContext context) {
        return Text("${this.widget.message}");
      }
    }
    

這也就是為什么State類定義的泛型需要跟之前創(chuàng)建的StatefulWidget子類保持一致的原因

三碍庵、StatelessWidget材蛛、StatefulWidget生命周期

  1. 生命周期的作用(意義)
  • 初始化一些數(shù)據(jù)圆到、狀態(tài)、變量
  • 發(fā)送網(wǎng)絡(luò)請求時機
  • 進行一些事件的監(jiān)聽
  • 管理內(nèi)存(手動銷毀)
  1. StatelessWidget生命周期
    這里比較簡單卑吭,只有兩個生命周期
  • 構(gòu)造函數(shù)被調(diào)用
  • 調(diào)用build方法
class TestWidget extends StatelessWidget {
  TestWidget() {
    print("構(gòu)造函數(shù)被創(chuàng)建");
  }

  @override
  Widget build(BuildContext context) {
    print("build方法被調(diào)用");
    return Container();
  }
}
  1. StatefulWidget生命周期


    image.png
  • 第一步:調(diào)用StatefulWidget子類的構(gòu)造方法
  • 第二步:調(diào)用StatefulWidget子類的createState方法
  • 第三步:調(diào)用State子類的構(gòu)造方法
  • 第四步 調(diào)用State子類的initState方法
    用于狀態(tài)的初始化芽淡,會在State類完成構(gòu)造之后,build方法執(zhí)行之前豆赏,進行執(zhí)行
    initState方法使用@mustCallSuper標記挣菲,必須調(diào)用父類initState方法
  • 第五步:執(zhí)行State子類的build方法
  • 最后,當前的Widget子類不再使用時掷邦,會執(zhí)行State子類的dispose方法白胀,進行內(nèi)存的釋放

此外:

  • didChangeDependencies方法 在initState方法執(zhí)行后,或者從其他對象中依賴一些數(shù)據(jù)發(fā)生改變時抚岗,比如inheritedWidget或杠,會執(zhí)行此方法
  • didUpdateWidget方法 當重新創(chuàng)建Widget子類(rebuild),會觸發(fā)state子類的didUpdateWidget方法

代碼如下所示:

class YWHomeContent extends StatefulWidget {
  YWHomeContent() {
    print("1.調(diào)用YWHomeContent的初始化方法");
  }
  @override
  _YWHomeContentState createState() {
    print("2.調(diào)用YWHomeContent的createState方法");
    return _YWHomeContentState();
  }
}

class _YWHomeContentState extends State<YWHomeContent> {
  _YWHomeContentState() {
    print("3.調(diào)用_YWHomeContentState的初始化方法");
  }

  @override
  void initState() {
    super.initState();
    print("4.調(diào)用_YWHomeContentState的initState方法");
  }

  @override
  void didChangeDependencies() {
    print("調(diào)用_YWHomeContentState的didChangeDependencies方法");
    super.didChangeDependencies();
  }

  @override
  void didUpdateWidget(covariant YWHomeContent oldWidget) {
    print("調(diào)用_YWHomeContentState的didUpdateWidget方法");
    super.didUpdateWidget(oldWidget);
  }

  @override
  Widget build(BuildContext context) {
    print("5.調(diào)用_YWHomeContentState的build方法");
    return Text("Hello World");
  }

  @override
  void dispose() {
    super.dispose();
    print("6.調(diào)用_YWHomeContentState的dispose方法");
  }
}
  • setState方法會在內(nèi)部觸發(fā)重新運行build方法宣蔚,根據(jù)最新的狀態(tài)返回新的控件向抢,所以狀態(tài)的修改需要在setState方法中進行
  1. StatefulWidget生命周期的復(fù)雜版
    在StatefulWidget普通生命周期的基礎(chǔ)上,還有一些復(fù)雜的過程未提及到
  • mounted
  • dirty state & clean state
    疑問:意義及用法胚委?

5.生命周期擴展

createState
當構(gòu)建一個 StatefulWidget 會立即調(diào)用該方法
@override
_MyStatefulPageState createState() {
print("lxf -- createState");
return _MyStatefulPageState();
}
initState
在 Widget 被創(chuàng)建出來并插入到樹中時被調(diào)用的方法挟鸠,只會被調(diào)用一次,等價于安卓的 onCreate() 或 iOS 中的 viewDidLoad()亩冬,所以在此時視圖還未被渲染艘希,但是 Widget 已經(jīng)被插入到樹中,一般用于執(zhí)行一些初始化操作

@override
void initState() {
super.initState();
print("lxf -- initState");

// 初始化操作
...
}

mounted
所有的 Widget 都擁有這個屬性硅急,當 buildContext 被分配且當前在樹中時覆享,該值為 true,直到調(diào)用 dispose 時重置為 false

addPostFrameCallback
單次 Frame(幀) 繪制回調(diào)营袜,在當前幀繪制完成后進行回調(diào)淹真,用于在 Widget 渲染完畢之后做一些操作,該回調(diào)只會進行一次连茧,如果需要再次監(jiān)聽則需要再次設(shè)置。
@override
void initState() {
super.initState();
print("lxf -- initState");

// 單次幀繪制回調(diào)(只會回調(diào)一次巍糯,如果要再次監(jiān)聽需要再設(shè)置)
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
print("lxf -- addPostFrameCallback");
});
}
如果希望在實時繪制幀時進行回調(diào)啸驯,則使用 addPersistentFrameCallback
// 實時幀繪制回調(diào)(每次繪制幀結(jié)束后都會回調(diào))
WidgetsBinding.instance?.addPersistentFrameCallback((timeStamp) {
print("lxf -- addPersistentFrameCallback");
});

didChangeDependencies
在 initState() 方法被執(zhí)行后立刻被調(diào)用,之后當依賴的 InheritedWidget 發(fā)生變化時祟峦,框架將會再次調(diào)用該方法將變化通知給當前對象罚斗。

build
在 didChangeDependencies 之后被調(diào)用,在該方法中會將 Widget 進行渲染宅楞,且由于再次渲染的操作是廉價的针姿,所以每次 UI 需要被渲染時該方都會被調(diào)用袱吆,但是為了避免影響渲染效率,請不要在這里做創(chuàng)建 Widget 的其它操作距淫!

didUpdateWidget
當父 Widget 發(fā)生改變并需要進行重繪時绞绒,該方法就會被調(diào)用,該方法還接收一個 oldWidget 參數(shù)榕暇,可以使用它與當前 Widget 進行比較來做一些額外的邏輯處理蓬衡。
@override
void didUpdateWidget(covariant LXFStatefulPage oldWidget) {
super.didUpdateWidget(oldWidget);
print("lxf -- didUpdateWidget");
}

deactivate
當 State 對象從樹中被移除時會調(diào)用該方法(包括從樹中移除又被插入到樹中其它位置的情況)

dispose
當 State 對象從樹中被移除并且不再被構(gòu)建時會調(diào)用該方法,常用于取消對 streams 的訂閱等釋放資源的操作

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末彤枢,一起剝皮案震驚了整個濱河市狰晚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌缴啡,老刑警劉巖壁晒,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異业栅,居然都是意外死亡秒咐,警方通過查閱死者的電腦和手機式镐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門反镇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人娘汞,你說我怎么就攤上這事歹茶。” “怎么了你弦?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵惊豺,是天一觀的道長。 經(jīng)常有香客問我禽作,道長尸昧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任旷偿,我火速辦了婚禮烹俗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘萍程。我一直安慰自己幢妄,他們只是感情好,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布茫负。 她就那樣靜靜地躺著蕉鸳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上潮尝,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天榕吼,我揣著相機與錄音,去河邊找鬼勉失。 笑死羹蚣,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的戴质。 我是一名探鬼主播度宦,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼告匠!你這毒婦竟也來了戈抄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤后专,失蹤者是張志新(化名)和其女友劉穎划鸽,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體戚哎,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡裸诽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了型凳。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丈冬。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖甘畅,靈堂內(nèi)的尸體忽然破棺而出埂蕊,到底是詐尸還是另有隱情,我是刑警寧澤疏唾,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布蓄氧,位于F島的核電站,受9級特大地震影響槐脏,放射性物質(zhì)發(fā)生泄漏喉童。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一顿天、第九天 我趴在偏房一處隱蔽的房頂上張望堂氯。 院中可真熱鬧,春花似錦牌废、人聲如沸咽白。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽片拍。三九已至屿脐,卻和暖如春娩践,著一層夾襖步出監(jiān)牢的瞬間陪腌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工莫绣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留畴蒲,地道東北人。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓对室,卻偏偏與公主長得像模燥,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子掩宜,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

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