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