Day05 - Flutter開發(fā)初體驗(yàn)

一篮洁、創(chuàng)建Flutter項(xiàng)目
創(chuàng)建Flutter項(xiàng)目有兩種方式:通過命令行創(chuàng)建 和 通過開發(fā)工具創(chuàng)建

  • 1.1涩维、在終端通過命令行創(chuàng)建(全新的APP)

    flutter create 項(xiàng)目的名字
    

    提示:

    • Flutter的名稱不要包含特殊的字符,另外不可以使用駝峰標(biāo)識(shí)(不要使用大寫)袁波,名字很長可以使用下劃線連接
    • 創(chuàng)建完之后使用自己喜歡的開發(fā)工具打開即可瓦阐,比如:Android SdudioVSCode

    創(chuàng)建擴(kuò)展: 語言的設(shè)置

    • flutter create -i swift -a kotlin 項(xiàng)目的名字
    • flutter create -i swift -a java 項(xiàng)目的名字
    • flutter create -i objc -a kottlin 項(xiàng)目的名字
    • flutter create -i objc -a java 項(xiàng)目的名字

    2>篷牌、混合項(xiàng)目的創(chuàng)建(混編到已有的Android / iOS工程內(nèi))

    • flutter create --template module 組件的名字

    3>睡蟋、Flutter Plugin的創(chuàng)建(Flutter平臺(tái)插件工程,包含Dart層與Native平臺(tái)層的實(shí)現(xiàn))

    • flutter create --template=plugin xxapp_plugin

    4>枷颊、Flutter Package(Flutter純Dart插件工程戳杀,僅包含Dart層的實(shí)現(xiàn)该面,往往定義一些公共Widget)

    • flutter create --template=package xxapp_package
  • 1.2、目錄結(jié)構(gòu)概述


    flutter 目錄結(jié)構(gòu)概述
    • dart_tool:記錄一些東西所在的位置以及它的一些版本信息信卡,不需要我們?nèi)ヅ渲酶糇海灰謩?dòng)去修改
    • idea: 記錄當(dāng)前項(xiàng)目的一些配置的
    • iml:也是對記錄對當(dāng)前項(xiàng)目的配置
    • Android:安卓的代碼
    • build:項(xiàng)目的構(gòu)建輸出目錄
    • iOS:iOS的代碼
    • lib:flutter 源碼的文件夾
    • test:主要是用來做一些測試的
    • gitignore:記錄當(dāng)前的一些忽略的
    • metadate:對flutter版本的一些記錄,不需要手動(dòng)去改
    • packages傍菇、pubspec.lock 都是導(dǎo)入到三方后生成的
    • pubspec.yaml:項(xiàng)目依賴配置文件類似于RN的 package.json
  • 1.3蚕泽、Flutter Hot Reload 熱重載Flutter Hot Restart 熱重啟 的區(qū)別

    Flutter Hot Reload 熱重載 和 Flutter Hot Restart 熱重啟

    • Flutter Hot Reload 熱重載:主要是執(zhí)行 build 方法
    • Flutter Hot Restart 熱重啟:重新運(yùn)行整個(gè)app(熱啟動(dòng))

    提示:冷啟動(dòng)是從未啟動(dòng)過app

二、Flutter代碼展示桥嗤,看不懂沒關(guān)系须妻,這篇博客僅僅是展示,下篇才開始學(xué)習(xí)

  • 2.1泛领、Hello World的實(shí)現(xiàn)
    我們在 main.dart 里面進(jìn)行從 0 到 1 編寫代碼

    import 'package:flutter/material.dart';
    
    main(List<String> args) {
         runApp(Text("Hello World", textDirection: TextDirection.ltr));
    }
    

    當(dāng)然荒吏,上面的代碼我們已經(jīng)實(shí)現(xiàn)了在界面上顯示Hello World:
    但是沒有居中,字體也有點(diǎn)性ㄐ绰更;
    這些問題,我們放到后面再來解決锡宋,先搞懂目前的幾行代碼儡湾;
    上面的代碼我們有一些比較熟悉,有一些并不清楚是什么:
    比如我們知道Dart程序的入口都是main函數(shù)执俩,而Flutter是Dart編寫的徐钠,所以入口也是main函數(shù);
    但是我們導(dǎo)入的Material是什么呢役首?
    另外尝丐,我們在main函數(shù)中調(diào)用了一個(gè)runApp()函數(shù)又是什么呢?
    下面衡奥,我們對不認(rèn)識(shí)的代碼進(jìn)行一些分析爹袁。

  • 2.2、代碼分析

    • 1>矮固、runApp 和 Widget
      runApp是Flutter內(nèi)部提供的一個(gè)函數(shù)失息,當(dāng)我們啟動(dòng)一個(gè)Flutter應(yīng)用程序時(shí)就是從調(diào)用這個(gè)函數(shù)開始的

      • 我們可以點(diǎn)到runApp的源碼辰晕,查看到該函數(shù)

      • 我們暫時(shí)不分析具體的源碼(因?yàn)槲野l(fā)現(xiàn)過多的理論,對于初學(xué)者來說并不友好)

        void runApp(Widget app) {
            ...省略代碼
        }
        

        該函數(shù)讓我們傳入一個(gè)東西:Widget校辩?
        我們先說Widget的翻譯:

        • Widget在國內(nèi)有很多的翻譯故黑;
          • 做過Android诗轻、iOS等開發(fā)的人群搔体,喜歡將它翻譯成控件计螺;
          • 做過Vue陈轿、React等開發(fā)的人群潜秋,喜歡將它翻譯成組件寨躁;
          • 如果我們使用Google放钦,Widget翻譯過來應(yīng)該是小部件;
          • 沒有說哪種翻譯一定是對的,或者一定是錯(cuò)的歇父,但是我個(gè)人更傾向于小部件或者組件;
        • Widget到底什么東西呢钳枕?
          • 我們學(xué)習(xí)Flutter,從一開始就可以有一個(gè)基本的認(rèn)識(shí):Flutter中萬物皆Widget(萬物皆可盤);
          • 在我們iOS或者Android開發(fā)中蝌借,我們的界面有很多種類的劃分:應(yīng)用(Application)缘圈、視圖控制器(View Controller)凿傅、活動(dòng)(Activity)缠犀、View(視圖)、Button(按鈕)等等箱残;
          • 但是在Flutter中滔迈,這些東西都是不同的Widget而已;
          • 也就是我們整個(gè)應(yīng)用程序中所看到的內(nèi)容幾乎都是Widget被辑,甚至是內(nèi)邊距的設(shè)置燎悍,我們也需要使用一個(gè)叫Padding的Widget來做;
        • runApp函數(shù)讓我們傳入的就是一個(gè)Widget:
          • 但是我們現(xiàn)在沒有Widget盼理,怎么辦呢谈山?
          • 我們可以導(dǎo)入Flutter默認(rèn)已經(jīng)給我們提供的Material庫,來使用其中的很多內(nèi)置Widget榜揖;
    • 2>勾哩、Material 設(shè)計(jì)風(fēng)格

      • material是什么呢?

        • material是Google公司推行的一套設(shè)計(jì)風(fēng)格举哟,或者叫設(shè)計(jì)語言、設(shè)計(jì)規(guī)范等迅矛;
        • 里面有非常多的設(shè)計(jì)規(guī)范妨猩,比如顏色、文字的排版秽褒、響應(yīng)動(dòng)畫與過度壶硅、填充等等威兜;
        • 在Flutter中高度集成了Material風(fēng)格的Widget;
        • 在我們的應(yīng)用中庐椒,我們可以直接使用這些Widget來創(chuàng)建我們的應(yīng)用(后面會(huì)用到很多)椒舵;
      • Text小部件分析:

        • 我們可以使用Text小部件來完成文字的顯示;

        • 我們發(fā)現(xiàn)Text小部件繼承自StatelessWidget约谈,StatelessWidget繼承自Widget笔宿;

        • 所以我們可以將Text小部件傳入到runApp函數(shù)中

        • 屬性非常多,但是我們已經(jīng)學(xué)習(xí)了Dart語法棱诱,所以你會(huì)發(fā)現(xiàn)只有this.data屬性是必須傳入的泼橘。

          class Text extends StatelessWidget {
              const Text(
              this.data, {
              Key key,
              this.style,
              this.strutStyle,
              this.textAlign,
              this.textDirection,
              this.locale,
              this.softWrap,
              this.overflow,
              this.textScaleFactor,
              this.maxLines,
              this.semanticsLabel,
              this.textWidthBasis,
              });
          }
          
      • StatelessWidget簡單介紹:

        • StatelessWidget繼承自Widget;

        • 后面我會(huì)更加詳細(xì)的介紹它的用法迈勋;

          abstract class StatelessWidget extends Widget {
             // ...省略代碼
          }
          
  • 2.3炬灭、代碼改進(jìn)

    • 1>、改進(jìn)界面樣式
      我們可能希望文字居中顯示靡菇,并且可以大一些重归;
      居中顯示: 需要使用另外一個(gè)Widget,Center厦凤;
      文字大一些: 需要給Text文本設(shè)置一些樣式鼻吮;
      我們修改代碼如下:
      我們在Text小部件外層包裝了一個(gè)Center部件,讓Text作為其child泳唠;
      并且狈网,我們給Text組件設(shè)置了一個(gè)屬性:style,對應(yīng)的值是TextStyle類型笨腥;

      import 'package:flutter/material.dart';
      
      main(List<String> args) {
          runApp(Center(
              child: Text(
                 "Hello World",
                 textDirection: TextDirection.ltr,
                 style: TextStyle(fontSize: 20),
              ),
           )
         );
      }
      
    • 2>拓哺、改進(jìn)界面結(jié)構(gòu)
      目前我們雖然可以顯示HelloWorld,但是我們發(fā)現(xiàn)最底部的背景是黑色脖母,并且我們的頁面并不夠結(jié)構(gòu)化士鸥。
      正常的App頁面應(yīng)該有一定的結(jié)構(gòu),比如通常都會(huì)有導(dǎo)航欄谆级,會(huì)有一些背景顏色
      在開發(fā)當(dāng)中烤礁,我們并不需要從零去搭建這種結(jié)構(gòu)化的界面,我們可以使用Material庫肥照,直接使用其中的一些封裝好的組件來完成一些結(jié)構(gòu)的搭建脚仔。
      我們通過下面的代碼來實(shí)現(xiàn):

      import 'package:flutter/material.dart';
      
      main(List<String> args) {
          runApp(
             MaterialApp(
                home: Scaffold(
                   appBar: AppBar(
                       title: Text("CODERWHY"),
                   ),
                   body: Center(
                       child: Text(
                       "Hello World",
                       textDirection: TextDirection.ltr,
                       style: TextStyle(fontSize: 36),
                   ),
                ),
             ),
          )
        );
      }
      
      • 在最外層包裹一個(gè)MaterialApp
        • 這意味著整個(gè)應(yīng)用我們都會(huì)采用MaterialApp風(fēng)格的一些東西,方便我們對應(yīng)用的設(shè)計(jì)舆绎,并且目前我們使用了其中兩個(gè)屬性鲤脏;
        • title:這個(gè)是定義在Android系統(tǒng)中打開多任務(wù)切換窗口時(shí)顯示的標(biāo)題;(暫時(shí)可以不寫)
        • home:是該應(yīng)用啟動(dòng)時(shí)顯示的頁面,我們傳入了一個(gè)Scaffold猎醇;
      • Scaffold是什么呢窥突?
        • 翻譯過來是腳手架,腳手架的作用就是搭建頁面的基本結(jié)構(gòu)硫嘶;
        • 所以我們給MaterialApp的home屬性傳入了一個(gè)Scaffold對象阻问,作為啟動(dòng)顯示的Widget;
        • Scaffold也有一些屬性沦疾,比如appBar和body称近;
        • appBar是用于設(shè)計(jì)導(dǎo)航欄的,我們傳入了一個(gè)title屬性曹鸠;
        • body是頁面的內(nèi)容部分煌茬,我們傳入了之前已經(jīng)創(chuàng)建好的Center中包裹的一個(gè)Text的Widget;
  • 2.4彻桃、代碼重構(gòu)

    • 1>坛善、創(chuàng)建自己的Widget
      很多學(xué)習(xí)Flutter的人,都會(huì)被Flutter的嵌套勸退邻眷,當(dāng)代碼嵌套過多時(shí)眠屎,結(jié)構(gòu)很容易看不清晰。

      • 這里有兩點(diǎn)我先說明一下:
        • 1肆饶、Flutter整個(gè)開發(fā)過程中就是形成一個(gè)Widget樹改衩,所以形成嵌套是很正常的。
        • 2驯镊、關(guān)于Flutter的代碼縮進(jìn)葫督,更多開發(fā)中我們使用的是2個(gè)空格(前端開發(fā)2個(gè)空格居多,你喜歡4個(gè)也沒問題)
      • 但是板惑,我們開發(fā)一個(gè)這么簡單的程序就出現(xiàn)如此多的嵌套橄镜,如果應(yīng)用程序更復(fù)雜呢?
        • 我們可以對我們的代碼進(jìn)行封裝冯乘,將它們封裝到自己的Widget中洽胶,創(chuàng)建自己的Widget;
      • 如何創(chuàng)建自己的Widget呢裆馒?
        • 在Flutter開發(fā)中姊氓,我們可以繼承自StatelessWidget或者StatefulWidget來創(chuàng)建自己的Widget類;
        • StatelessWidget: 沒有狀態(tài)改變的Widget喷好,通常這種Widget僅僅是做一些展示工作而已翔横;
        • StatefulWidget: 需要保存狀態(tài),并且可能出現(xiàn)狀態(tài)改變的Widget梗搅;

      在上面的案例中對代碼的重構(gòu)棕孙,我們使用StatelessWidget即可,所以我們接下來學(xué)習(xí)一下如果利用StatelessWidget來對我們的代碼進(jìn)行重構(gòu)些膨;
      StatefulWidget我們放到后面的一個(gè)案例中來學(xué)習(xí)蟀俊;

    • 2>、StatelessWidget

      • StatelessWidget通常是一些沒有狀態(tài)(State订雾,也可以理解成data)需要維護(hù)的Widget:
        • 它們的數(shù)據(jù)通常是直接寫死(放在Widget中的數(shù)據(jù)肢预,必須被定義為final,為什么呢洼哎?我在下一個(gè)章節(jié)講解StatefulWidget會(huì)講到)烫映;
        • 從parent widget中傳入的而且一旦傳入就不可以修改;
        • 從InheritedWidget獲取來使用的數(shù)據(jù)(這個(gè)放到后面會(huì)講解)噩峦;
      • 我們來看一下創(chuàng)建一個(gè)StatelessWidget的格式:
        • 讓自己創(chuàng)建的Widget繼承自StatelessWidget锭沟;

        • StatelessWidget包含一個(gè)必須重寫的方法:build方法;

          class MyStatelessWidget extends StatelessWidget {
             @override
             Widget build(BuildContext context) {
                 return <返回我們的Widget要渲染的Widget识补,比如一個(gè)Text Widget>;
             }
          }
          
      • build方法的解析:
        • Flutter在拿到我們自己創(chuàng)建的StatelessWidget時(shí)族淮,就會(huì)執(zhí)行它的build方法;
        • 我們需要在build方法中告訴Flutter凭涂,我們的Widget希望渲染什么元素祝辣,比如一個(gè)Text Widget;
        • StatelessWidget沒辦法主動(dòng)去執(zhí)行build方法切油,當(dāng)我們使用的數(shù)據(jù)發(fā)生改變時(shí)蝙斜,build方法會(huì)被重新執(zhí)行;
      • build方法什么情況下被執(zhí)行呢 澎胡?
        • 當(dāng)我們的StatelessWidget第一次被插入到Widget樹中時(shí)(也就是第一次被創(chuàng)建時(shí))孕荠;
        • 當(dāng)我們的父Widget(parent widget)發(fā)生改變時(shí),子Widget會(huì)被重新構(gòu)建攻谁;
        • 如果我們的Widget依賴InheritedWidget的一些數(shù)據(jù)稚伍,InheritedWidget數(shù)據(jù)發(fā)生改變時(shí);
    • 3>巢株、重構(gòu)案例代碼



      現(xiàn)在我們就可以通過StatelessWidget來對我們的代碼進(jìn)行重構(gòu)了

      • 因?yàn)槲覀兊恼麄€(gè)代碼都是一些數(shù)據(jù)展示槐瑞,沒有數(shù)據(jù)的改變,使用StatelessWidget即可阁苞;
      • 另外困檩,為了體現(xiàn)更好的封裝性,我對代碼進(jìn)行了兩層的拆分那槽,讓代碼結(jié)構(gòu)看起來更加清晰悼沿;(具體的拆分方式哎壳,我會(huì)在后面的案例中不斷的體現(xiàn)出來董栽,目前我們先拆分兩層)

      重構(gòu)后的代碼如下:

      import 'package:flutter/material.dart';
      
      main(List<String> args) {
          runApp(MyApp());
      }
      
      class MyApp extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
            return MaterialApp(
                home: Scaffold(
                  appBar: AppBar(
                  title: Text("CODERWHY"),
                ),
                body: HomeContent(),
            ),
          )
        }
      }
      
      class HomeContent extends StatelessWidget {
         @override
         Widget build(BuildContext context) {
            return Center(
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                      Checkbox(
                         value: true,
                         onChanged: (value) => print("Hello World")
                   ),
                   Text(
                      "同意協(xié)議",
                      textDirection: TextDirection.ltr,
                      style: TextStyle(fontSize: 20),
                   )
                 ],
               ),
            );
         }
      }
      
  • 2.5逢净、StateFulWidget (有狀態(tài)的)
    在開發(fā)中,某些Widget情況下我們展示的數(shù)據(jù)并不是一層不變的:
    比如Flutter默認(rèn)程序中的計(jì)數(shù)器案例陕悬,點(diǎn)擊了+號按鈕后,顯示的數(shù)字需要+1喳坠;
    比如在開發(fā)中杈曲,我們會(huì)進(jìn)行下拉刷新、上拉加載更多非驮,這時(shí)數(shù)據(jù)也會(huì)發(fā)生變化交汤;
    而StatelessWidget通常用來展示哪些數(shù)據(jù)固定不變的,如果數(shù)據(jù)會(huì)發(fā)生改變劫笙,我們使用StatefulWidget芙扎;

    • 1>、StatefulWidget介紹
      為什么選擇StatefulWidget呢填大?
      在示例代碼中戒洼,當(dāng)我們點(diǎn)擊按鈕時(shí),界面上顯示的數(shù)據(jù)會(huì)發(fā)生改變允华;
      這時(shí)圈浇,我們需要一個(gè)變量來記錄當(dāng)前的狀態(tài),再把這個(gè)變量顯示到某個(gè)Text Widget上例获;
      并且每次變量發(fā)生改變時(shí)汉额,我們對應(yīng)的Text上顯示的內(nèi)容也要發(fā)生改變;
      但是有一個(gè)問題榨汤,我之前說過定義到Widget中的數(shù)據(jù)都是不可變的蠕搜,必須定義為final,為什么呢收壕?
      這次因?yàn)镕lutter在設(shè)計(jì)的時(shí)候就決定了一旦Widget中展示的數(shù)據(jù)發(fā)生變化妓灌,就重新構(gòu)建整個(gè)Widget;Flutter通過一些機(jī)制來限定定義到Widget中的成員變量必須是final的蜜宪;

      • Flutter如何做到我們在開發(fā)中定義到Widget中的數(shù)據(jù)一定是final的呢虫埂?,我們來看一下Widget的源碼

        @immutable
        abstract class Widget extends DiagnosticableTree {
             // ...省略代碼
        }
        

        @immutable 注解標(biāo)明的類或者子類都必須是不可變的

      提示:定義到Widget中的數(shù)據(jù)一定是不可變的,需要使用final來修飾

    • 2>圃验、如何存儲(chǔ)Widget狀態(tài)掉伏?

      • 既然Widget是不可變,那么StatefulWidget如何來存儲(chǔ)可變的狀態(tài)呢澳窑?
        • StatelessWidget無所謂斧散,因?yàn)樗锩娴臄?shù)據(jù)通常是直接定義完后就不修改的。
        • 但StatefulWidget需要有狀態(tài)(可以理解成變量)的改變摊聋,這如何做到呢鸡捐?
      • Flutter將StatefulWidget設(shè)計(jì)成了兩個(gè)類:
        • 也就是你創(chuàng)建StatefulWidget時(shí)必須創(chuàng)建兩個(gè)類:
        • 一個(gè)類繼承自StatefulWidget,作為Widget樹的一部分麻裁;
        • 一個(gè)類繼承自State箍镜,用于記錄StatefulWidget會(huì)變化的狀態(tài)源祈,并且根據(jù)狀態(tài)的變化,構(gòu)建出新的Widget色迂;
      • 創(chuàng)建一個(gè)StatefulWidget香缺,我們通常會(huì)按照如下格式來做:
        • 當(dāng)Flutter在構(gòu)建Widget Tree時(shí),會(huì)獲取State的實(shí)例脚草,并且它調(diào)用build方法去獲取StatefulWidget希望構(gòu)建的Widget赫悄;

        • 那么,我們就可以將需要保存的狀態(tài)保存在MyState中馏慨,因?yàn)樗强勺兊模?/p>

          class MyStatefulWidget extends StatefulWidget {
               @override
               State<StatefulWidget> createState() {
                   // 將創(chuàng)建的State返回
                   return MyState();
               }
          }
          
          class MyState extends State<MyStatefulWidget> {
               @override
               Widget build(BuildContext context) {
                   return <構(gòu)建自己的Widget>;
               }
          }
          

        提示:Flutter這樣設(shè)計(jì)的目的是:在Flutter中,只要數(shù)據(jù)改變了Widget就需要重新構(gòu)建(rebuild)

  • 2.6姑隅、StatefulWidget案例
    我們通過一個(gè)案例來練習(xí)一下StatefulWidget写隶,還是之前的計(jì)數(shù)器案例,但是我們按照自己的方式進(jìn)行一些改進(jìn)讲仰。
    案例效果以及布局如下:

    • 在這個(gè)案例中慕趴,有很多布局對于我們來說有些復(fù)雜,我們后面會(huì)詳細(xì)學(xué)習(xí)鄙陡,建議大家根據(jù)我的代碼一步步寫出來來熟悉Flutter開發(fā)模式冕房;

    • Column小部件:之前我們已經(jīng)用過,當(dāng)有垂直方向布局時(shí)趁矾,我們就使用它耙册;

    • Row小部件:之前也用過,當(dāng)時(shí)水平方向布局時(shí)毫捣,我們就使用它详拙;

    • RaiseButton小部件:可以創(chuàng)建一個(gè)按鈕,并且其中有一個(gè)onPress屬性是傳入一個(gè)回調(diào)函數(shù)蔓同,當(dāng)按鈕點(diǎn)擊時(shí)被回調(diào)饶辙;


    • 1>、 創(chuàng)建StatefulWidget斑粱,下面我們來看看代碼實(shí)現(xiàn):

      • 為當(dāng)點(diǎn)擊按鈕時(shí)弃揽,數(shù)字會(huì)發(fā)生變化,所以我們需要使用一個(gè)StatefulWidget则北,所以我們需要?jiǎng)?chuàng)建兩個(gè)類矿微;

      • MyCounterWidget繼承自StatefulWidget,里面需要實(shí)現(xiàn)createState方法咒锻;

      • MyCounterState繼承自State冷冗,里面實(shí)現(xiàn)build方法,并且可以定義一些成員變量

        class MyCounterWidget extends StatefulWidget {
          @override
          State<StatefulWidget> createState() {
               // 將創(chuàng)建的State返回
               return MyCounterState();
          }
        }
        
        class MyCounterState extends State<MyCounterWidget> {
            int counter = 0;
        
           @override
           Widget build(BuildContext context) {
               return Center(
                   child: Text("當(dāng)前計(jì)數(shù):$counter", style: TextStyle(fontSize: 30),),
               );
           }
        }
        
    • 2>惑艇、實(shí)現(xiàn)按鈕的布局

      class MyCounterState extends State<MyCounterWidget> {
          int counter = 0;
      
          @override
          Widget build(BuildContext context) {
             return Center(
                 child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                        Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: <Widget>[
                              RaisedButton(
                                 color: Colors.redAccent,
                                 child: Text("+1", style: TextStyle(fontSize: 18, color: Colors.white),),
                                 onPressed: () {
      
                                 },
                              ),
                              RaisedButton(
                                 color: Colors.orangeAccent,
                                 child: Text("-1", style: TextStyle(fontSize: 18, color: Colors.white),),
                                 onPressed: () {
      
                                 },
                              )
                          ],
                        ),
                       Text("當(dāng)前計(jì)數(shù):$counter", style: TextStyle(fontSize: 30),)
                      ],
                 ),
             );
         }
      }
      
    • 3>蒿辙、按鈕點(diǎn)擊狀態(tài)改變

      • 我們現(xiàn)在要監(jiān)聽狀態(tài)的改變拇泛,當(dāng)狀態(tài)改變時(shí)要修改counter變量:
        • 但是,直接修改變量可以改變界面嗎思灌?不可以俺叭。
        • 這是因?yàn)镕lutter并不知道我們的數(shù)據(jù)發(fā)生了改變,需要來重新構(gòu)建我們界面中的Widget泰偿;
      • 如何可以讓Flutter知道我們的狀態(tài)發(fā)生改變了熄守,重新構(gòu)建我們的Widget呢?
        • 我們需要調(diào)用一個(gè)State中默認(rèn)給我們提供的setState方法耗跛;

        • 可以在其中的回調(diào)函數(shù)中修改我們的變量裕照;

          onPressed: () {
             setState(() {  
                counter++;  
             });   
          },   
          
  • 2.7、StatefulWidget 生命周期

    • 1>调塌、生命周期的理解

      • 什么是生命周期呢晋南?
        • 客戶端開發(fā):iOS開發(fā)中我們需要知道UIViewController從創(chuàng)建到銷毀的整個(gè)過程,Android開發(fā)中我們需要知道Activity從創(chuàng)建到銷毀的整個(gè)過程羔砾。以便在不同的生命周期方法中完成不同的操作负间;
        • 前端開發(fā)中:Vue、React開發(fā)中組件也都有自己的生命周期姜凄,在不同的生命周期中我們可以做不同的操作政溃;
      • Flutter小部件的生命周期:
        • StatelessWidget可以由父Widget直接傳入值,調(diào)用build方法來構(gòu)建态秧,整個(gè)過程非常簡單董虱;
        • 而StatefulWidget需要通過State來管理其數(shù)據(jù),并且還要監(jiān)控狀態(tài)的改變決定是否重新build整個(gè)Widget屿聋;
        • 所以空扎,我們主要討論StatefulWidget的生命周期,也就是它從創(chuàng)建到銷毀的整個(gè)過程润讥;
    • 2>转锈、生命周期
      那么StatefulWidget有哪些生命周期的回調(diào)呢?它們分別在什么情況下執(zhí)行呢楚殿?
      atefulWidget本身由兩個(gè)類組成的:StatefulWidget和State撮慨,我們分開進(jìn)行分析
      首先,執(zhí)行StatefulWidget中相關(guān)的方法:

      • 1脆粥、執(zhí)行StatefulWidget的構(gòu)造函數(shù)(Constructor)來創(chuàng)建出StatefulWidget砌溺;
      • 2、執(zhí)行StatefulWidget的createState方法变隔,來創(chuàng)建一個(gè)維護(hù)StatefulWidget的State對象规伐;

      其次,調(diào)用createState創(chuàng)建State對象時(shí)匣缘,執(zhí)行State類的相關(guān)方法:

      • 1猖闪、執(zhí)行State類的構(gòu)造方法(Constructor)來創(chuàng)建State對象鲜棠;
      • 2、執(zhí)行initState培慌,我們通常會(huì)在這個(gè)方法中執(zhí)行一些數(shù)據(jù)初始化的操作豁陆,或者也可能會(huì)發(fā)送網(wǎng)絡(luò)請求;
      • 3吵护、執(zhí)行didChangeDependencies方法盒音,這個(gè)方法在兩種情況下會(huì)調(diào)用
        • 情況一:調(diào)用initState會(huì)調(diào)用;
        • 情況二:從其他對象中依賴一些數(shù)據(jù)發(fā)生改變時(shí)馅而,比如前面我們提到的InheritedWidget祥诽;
      • 4、Flutter執(zhí)行build方法用爪,來看一下我們當(dāng)前的Widget需要渲染哪些Widget原押;
      • 5、當(dāng)前的Widget不再使用時(shí)偎血,會(huì)調(diào)用dispose進(jìn)行銷毀;
      • 6盯漂、手動(dòng)調(diào)用setState方法颇玷,會(huì)根據(jù)最新的狀態(tài)(數(shù)據(jù))來重新調(diào)用build方法,構(gòu)建對應(yīng)的Widgets就缆;
      • 7帖渠、執(zhí)行didUpdateWidget方法是在當(dāng)父Widget觸發(fā)重建(rebuild)時(shí),系統(tǒng)會(huì)調(diào)用didUpdateWidget方法竭宰;

      過代碼進(jìn)行演示:

      import 'package:flutter/material.dart';
      
      main(List<String> args) {
          runApp(MyApp());
      }
      
      class MyApp extends StatelessWidget {
          @override
          Widget build(BuildContext context) {
               return MaterialApp(
                   home: Scaffold(
                     appBar: AppBar(
                         title: Text("HelloWorld"),
                     ),
                   body: HomeBody(),
                ),
           );
        }
      }
      
      class HomeBody extends StatelessWidget {
         @override
         Widget build(BuildContext context) {
            print("HomeBody build");
            return MyCounterWidget();
         }
      }
      
      class MyCounterWidget extends StatefulWidget {
      
         MyCounterWidget() {
             print("執(zhí)行了MyCounterWidget的構(gòu)造方法");
         }
      
         @override
         State<StatefulWidget> createState() {
              print("執(zhí)行了MyCounterWidget的createState方法");
              // 將創(chuàng)建的State返回
              return MyCounterState();
          }
      }
      
      class MyCounterState extends State<MyCounterWidget> {
           int counter = 0;
      
           MyCounterState() {
              print("執(zhí)行MyCounterState的構(gòu)造方法");
           }
      
           @override
           void initState() {
               super.initState();
               print("執(zhí)行MyCounterState的init方法");
           }
      
           @override
           void didChangeDependencies() {
              // TODO: implement didChangeDependencies
              super.didChangeDependencies();
              print("執(zhí)行MyCounterState的didChangeDependencies方法");
           }
      
           @override
           Widget build(BuildContext context) {
               print("執(zhí)行執(zhí)行MyCounterState的build方法");
               return Center(
                     child: Column(
                       mainAxisAlignment: MainAxisAlignment.center,
                       children: <Widget>[
                            Row(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: <Widget>[
                                RaisedButton(
                                   color: Colors.redAccent,
                                   child: Text("+1", style: TextStyle(fontSize: 18, color: Colors.white),),
                                  onPressed: () {
                                     setState(() {
                                         counter++;
                                     });
                                  },
                                ),
                               RaisedButton(
                                   color: Colors.orangeAccent,
                                   child: Text("-1", style: TextStyle(fontSize: 18, color: Colors.white),),
                                   onPressed: () {
                                       setState(() {
                                          counter--;
                                       });
                                   },
                               )
                            ],
                         ),
                        Text("當(dāng)前計(jì)數(shù):$counter", style: TextStyle(fontSize: 30),)
                    ],
                ),
            );
          }
      
         @override
         void didUpdateWidget(MyCounterWidget oldWidget) {
                super.didUpdateWidget(oldWidget);
                 print("執(zhí)行MyCounterState的didUpdateWidget方法");
         }
      
         @override
         void dispose() {
            super.dispose();
            print("執(zhí)行MyCounterState的dispose方法");
         }
      }
      

      打印結(jié)果如下:

      flutter: HomeBody build
      flutter: 執(zhí)行了MyCounterWidget的構(gòu)造方法
      flutter: 執(zhí)行了MyCounterWidget的createState方法
      flutter: 執(zhí)行MyCounterState的構(gòu)造方法
      flutter: 執(zhí)行MyCounterState的init方法
      flutter: 執(zhí)行MyCounterState的didChangeDependencies方法
      flutter: 執(zhí)行執(zhí)行MyCounterState的build方法
      
      // 注意:Flutter會(huì)build所有的組件兩次(查了GitHub空郊、Stack Overflow,目前沒查到原因)
      flutter: HomeBody build
      flutter: 執(zhí)行了MyCounterWidget的構(gòu)造方法
      flutter: 執(zhí)行MyCounterState的didUpdateWidget方法
      flutter: 執(zhí)行執(zhí)行MyCounterState的build方法
      

      當(dāng)我們改變狀態(tài)切揭,手動(dòng)執(zhí)行setState方法后會(huì)打印如下結(jié)果:

      flutter: 執(zhí)行執(zhí)行MyCounterState的build方法
      
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末狞甚,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子廓旬,更是在濱河造成了極大的恐慌哼审,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件孕豹,死亡現(xiàn)場離奇詭異涩盾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)励背,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進(jìn)店門春霍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人叶眉,你說我怎么就攤上這事址儒∏奂希” “怎么了?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵离福,是天一觀的道長杖狼。 經(jīng)常有香客問我,道長妖爷,這世上最難降的妖魔是什么蝶涩? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮絮识,結(jié)果婚禮上绿聘,老公的妹妹穿的比我還像新娘。我一直安慰自己次舌,他們只是感情好熄攘,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著彼念,像睡著了一般挪圾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上逐沙,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天哲思,我揣著相機(jī)與錄音,去河邊找鬼吩案。 笑死棚赔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的徘郭。 我是一名探鬼主播靠益,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼残揉!你這毒婦竟也來了胧后?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤冲甘,失蹤者是張志新(化名)和其女友劉穎绩卤,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體江醇,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡濒憋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了陶夜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凛驮。...
    茶點(diǎn)故事閱讀 40,015評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖条辟,靈堂內(nèi)的尸體忽然破棺而出黔夭,到底是詐尸還是另有隱情宏胯,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布本姥,位于F島的核電站肩袍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏婚惫。R本人自食惡果不足惜氛赐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望先舷。 院中可真熱鬧艰管,春花似錦、人聲如沸蒋川。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捺球。三九已至缸浦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間氮兵,已是汗流浹背餐济。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留胆剧,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓醉冤,卻偏偏與公主長得像秩霍,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子蚁阳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評論 2 355