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

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

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

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

    提示:

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

    創(chuàng)建擴(kuò)展: 語(yǔ)言的設(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:也是對(duì)記錄對(duì)當(dāng)前項(xiàng)目的配置
    • Android:安卓的代碼
    • build:項(xiàng)目的構(gòu)建輸出目錄
    • iOS:iOS的代碼
    • lib:flutter 源碼的文件夾
    • test:主要是用來(lái)做一些測(cè)試的
    • gitignore:記錄當(dāng)前的一些忽略的
    • metadate:對(duì)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)過(guò)app

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

  • 2.1、Hello World的實(shí)現(xiàn)
    我們?cè)?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:
    但是沒(méi)有居中,字體也有點(diǎn)?。?br> 這些問(wèn)題,我們放到后面再來(lái)解決,先搞懂目前的幾行代碼;
    上面的代碼我們有一些比較熟悉,有一些并不清楚是什么:
    比如我們知道Dart程序的入口都是main函數(shù),而Flutter是Dart編寫的,所以入口也是main函數(shù);
    但是我們導(dǎo)入的Material是什么呢?
    另外,我們?cè)趍ain函數(shù)中調(diào)用了一個(gè)runApp()函數(shù)又是什么呢?
    下面,我們對(duì)不認(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ù)開(kāi)始的

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

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

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

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

        • Widget在國(guó)內(nèi)有很多的翻譯;
          • 做過(guò)Android、iOS等開(kāi)發(fā)的人群,喜歡將它翻譯成控件;
          • 做過(guò)Vue、React等開(kāi)發(fā)的人群,喜歡將它翻譯成組件;
          • 如果我們使用Google,Widget翻譯過(guò)來(lái)應(yīng)該是小部件;
          • 沒(méi)有說(shuō)哪種翻譯一定是對(duì)的,或者一定是錯(cuò)的,但是我個(gè)人更傾向于小部件或者組件;
        • Widget到底什么東西呢?
          • 我們學(xué)習(xí)Flutter,從一開(kāi)始就可以有一個(gè)基本的認(rèn)識(shí):Flutter中萬(wàn)物皆Widget(萬(wàn)物皆可盤);
          • 在我們iOS或者Android開(kāi)發(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來(lái)做;
        • runApp函數(shù)讓我們傳入的就是一個(gè)Widget:
          • 但是我們現(xiàn)在沒(méi)有Widget,怎么辦呢?
          • 我們可以導(dǎo)入Flutter默認(rèn)已經(jīng)給我們提供的Material庫(kù),來(lái)使用其中的很多內(nèi)置Widget;
    • 2>、Material 設(shè)計(jì)風(fēng)格

      • material是什么呢?

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

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

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

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

        • 屬性非常多,但是我們已經(jīng)學(xué)習(xí)了Dart語(yǔ)法,所以你會(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簡(jiǎn)單介紹:

        • StatelessWidget繼承自Widget;

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

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

    • 1>、改進(jìn)界面樣式
      我們可能希望文字居中顯示,并且可以大一些;
      居中顯示: 需要使用另外一個(gè)Widget,Center;
      文字大一些: 需要給Text文本設(shè)置一些樣式;
      我們修改代碼如下:
      我們?cè)赥ext小部件外層包裝了一個(gè)Center部件,讓Text作為其child;
      并且,我們給Text組件設(shè)置了一個(gè)屬性:style,對(duì)應(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)最底部的背景是黑色,并且我們的頁(yè)面并不夠結(jié)構(gòu)化。
      正常的App頁(yè)面應(yīng)該有一定的結(jié)構(gòu),比如通常都會(huì)有導(dǎo)航欄,會(huì)有一些背景顏色
      在開(kāi)發(fā)當(dāng)中,我們并不需要從零去搭建這種結(jié)構(gòu)化的界面,我們可以使用Material庫(kù),直接使用其中的一些封裝好的組件來(lái)完成一些結(jié)構(gòu)的搭建。
      我們通過(guò)下面的代碼來(lái)實(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)格的一些東西,方便我們對(duì)應(yīng)用的設(shè)計(jì),并且目前我們使用了其中兩個(gè)屬性;
        • title:這個(gè)是定義在Android系統(tǒng)中打開(kāi)多任務(wù)切換窗口時(shí)顯示的標(biāo)題;(暫時(shí)可以不寫)
        • home:是該應(yīng)用啟動(dòng)時(shí)顯示的頁(yè)面,我們傳入了一個(gè)Scaffold;
      • Scaffold是什么呢?
        • 翻譯過(guò)來(lái)是腳手架,腳手架的作用就是搭建頁(yè)面的基本結(jié)構(gòu);
        • 所以我們給MaterialApp的home屬性傳入了一個(gè)Scaffold對(duì)象,作為啟動(dòng)顯示的Widget;
        • Scaffold也有一些屬性,比如appBar和body;
        • appBar是用于設(shè)計(jì)導(dǎo)航欄的,我們傳入了一個(gè)title屬性;
        • body是頁(yè)面的內(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)代碼嵌套過(guò)多時(shí),結(jié)構(gòu)很容易看不清晰。

      • 這里有兩點(diǎn)我先說(shuō)明一下:
        • 1、Flutter整個(gè)開(kāi)發(fā)過(guò)程中就是形成一個(gè)Widget樹(shù),所以形成嵌套是很正常的。
        • 2、關(guān)于Flutter的代碼縮進(jìn),更多開(kāi)發(fā)中我們使用的是2個(gè)空格(前端開(kāi)發(fā)2個(gè)空格居多,你喜歡4個(gè)也沒(méi)問(wèn)題)
      • 但是,我們開(kāi)發(fā)一個(gè)這么簡(jiǎn)單的程序就出現(xiàn)如此多的嵌套,如果應(yīng)用程序更復(fù)雜呢?
        • 我們可以對(duì)我們的代碼進(jìn)行封裝,將它們封裝到自己的Widget中,創(chuàng)建自己的Widget;
      • 如何創(chuàng)建自己的Widget呢?
        • 在Flutter開(kāi)發(fā)中,我們可以繼承自StatelessWidget或者StatefulWidget來(lái)創(chuàng)建自己的Widget類;
        • StatelessWidget: 沒(méi)有狀態(tài)改變的Widget,通常這種Widget僅僅是做一些展示工作而已;
        • StatefulWidget: 需要保存狀態(tài),并且可能出現(xiàn)狀態(tài)改變的Widget;

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

    • 2>、StatelessWidget

      • StatelessWidget通常是一些沒(méi)有狀態(tài)(State,也可以理解成data)需要維護(hù)的Widget:
        • 它們的數(shù)據(jù)通常是直接寫死(放在Widget中的數(shù)據(jù),必須被定義為final,為什么呢?我在下一個(gè)章節(jié)講解StatefulWidget會(huì)講到);
        • 從parent widget中傳入的而且一旦傳入就不可以修改;
        • 從InheritedWidget獲取來(lái)使用的數(shù)據(jù)(這個(gè)放到后面會(huì)講解);
      • 我們來(lái)看一下創(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沒(méi)辦法主動(dòng)去執(zhí)行build方法,當(dāng)我們使用的數(shù)據(jù)發(fā)生改變時(shí),build方法會(huì)被重新執(zhí)行;
      • build方法什么情況下被執(zhí)行呢 ?
        • 當(dāng)我們的StatelessWidget第一次被插入到Widget樹(shù)中時(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)在我們就可以通過(guò)StatelessWidget來(lái)對(duì)我們的代碼進(jìn)行重構(gòu)了

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

      重構(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)的)
    在開(kāi)發(fā)中,某些Widget情況下我們展示的數(shù)據(jù)并不是一層不變的:
    比如Flutter默認(rèn)程序中的計(jì)數(shù)器案例,點(diǎn)擊了+號(hào)按鈕后,顯示的數(shù)字需要+1;
    比如在開(kāi)發(fā)中,我們會(huì)進(jìn)行下拉刷新、上拉加載更多,這時(shí)數(shù)據(jù)也會(huì)發(fā)生變化;
    而StatelessWidget通常用來(lái)展示哪些數(shù)據(jù)固定不變的,如果數(shù)據(jù)會(huì)發(fā)生改變,我們使用StatefulWidget;

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

      • Flutter如何做到我們?cè)陂_(kāi)發(fā)中定義到Widget中的數(shù)據(jù)一定是final的呢?,我們來(lái)看一下Widget的源碼

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

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

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

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

      • 既然Widget是不可變,那么StatefulWidget如何來(lái)存儲(chǔ)可變的狀態(tài)呢?
        • StatelessWidget無(wú)所謂,因?yàn)樗锩娴臄?shù)據(jù)通常是直接定義完后就不修改的。
        • 但StatefulWidget需要有狀態(tài)(可以理解成變量)的改變,這如何做到呢?
      • Flutter將StatefulWidget設(shè)計(jì)成了兩個(gè)類:
        • 也就是你創(chuàng)建StatefulWidget時(shí)必須創(chuàng)建兩個(gè)類:
        • 一個(gè)類繼承自StatefulWidget,作為Widget樹(shù)的一部分;
        • 一個(gè)類繼承自State,用于記錄StatefulWidget會(huì)變化的狀態(tài),并且根據(jù)狀態(tài)的變化,構(gòu)建出新的Widget;
      • 創(chuàng)建一個(gè)StatefulWidget,我們通常會(huì)按照如下格式來(lái)做:
        • 當(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案例
    我們通過(guò)一個(gè)案例來(lái)練習(xí)一下StatefulWidget,還是之前的計(jì)數(shù)器案例,但是我們按照自己的方式進(jìn)行一些改進(jìn)。
    案例效果以及布局如下:

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

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

    • Row小部件:之前也用過(guò),當(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,下面我們來(lái)看看代碼實(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īng)狀態(tài)的改變,當(dāng)狀態(tài)改變時(shí)要修改counter變量:
        • 但是,直接修改變量可以改變界面嗎?不可以。
        • 這是因?yàn)镕lutter并不知道我們的數(shù)據(jù)發(fā)生了改變,需要來(lái)重新構(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>、生命周期的理解

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

      • 1、執(zhí)行StatefulWidget的構(gòu)造函數(shù)(Constructor)來(lái)創(chuàng)建出StatefulWidget;
      • 2、執(zhí)行StatefulWidget的createState方法,來(lái)創(chuàng)建一個(gè)維護(hù)StatefulWidget的State對(duì)象;

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

      • 1、執(zhí)行State類的構(gòu)造方法(Constructor)來(lái)創(chuàng)建State對(duì)象;
      • 2、執(zhí)行initState,我們通常會(huì)在這個(gè)方法中執(zhí)行一些數(shù)據(jù)初始化的操作,或者也可能會(huì)發(fā)送網(wǎng)絡(luò)請(qǐng)求;
      • 3、執(zhí)行didChangeDependencies方法,這個(gè)方法在兩種情況下會(huì)調(diào)用
        • 情況一:調(diào)用initState會(huì)調(diào)用;
        • 情況二:從其他對(duì)象中依賴一些數(shù)據(jù)發(fā)生改變時(shí),比如前面我們提到的InheritedWidget;
      • 4、Flutter執(zhí)行build方法,來(lái)看一下我們當(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ù))來(lái)重新調(diào)用build方法,構(gòu)建對(duì)應(yīng)的Widgets;
      • 7、執(zhí)行didUpdateWidget方法是在當(dāng)父Widget觸發(fā)重建(rebuild)時(shí),系統(tǒng)會(huì)調(diào)用didUpdateWidget方法;

      過(guò)代碼進(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,目前沒(méi)查到原因)
      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)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容