一、創(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 Sdudio、VSCode
創(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
- Flutter的名稱不要包含特殊的字符,另外不可以使用駝峰標(biāo)識(shí)(不要使用大寫),名字很長(zhǎng)可以使用
-
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;
- Widget在國(guó)內(nèi)有很多的翻譯;
-
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;
- 在最外層包裹一個(gè)MaterialApp
-
-
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í); - 這里有兩點(diǎn)我先說(shuō)明一下:
-
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í);
- StatelessWidget通常是一些沒(méi)有狀態(tài)(State,也可以理解成data)需要維護(hù)的Widget:
-
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)
- 既然Widget是不可變,那么StatefulWidget如何來(lái)存儲(chǔ)可變的狀態(tài)呢?
-
-
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++; }); },
- 我們現(xiàn)在要監(jiān)聽(tīng)狀態(tài)的改變,當(dāng)狀態(tài)改變時(shí)要修改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方法
-



