文章目錄
-
-
- 一、目标
- 二、建立計數器項目
-
- 2.1 建立項目,設定項目名及配置SDK
- 2.2 設定包名
- 2.3 運作項目
- 2.4 計數器
- 三、項目結構分析
-
- 3.1 導包
- 3.2 應用入口
- 3.3 應用結構
- 3.4 首頁
- 3.5 _MyHomePageState詳解
- 3.6 流程小結
- 四、解惑build為何放在State中
-
一、目标
通過一個計數器示例,對
Flutter
應用程式結構有個基本了解,對其概念和技術有一定的認識,也友善後續對
Flutter
其他概念和技術的學習。
二、建立計數器項目
此過程比較簡單,通過
Android Studio
建立新的
Flutter
工程即可。
2.1 建立項目,設定項目名及配置SDK
2.2 設定包名
在此,僅勾選了
Kotlin support
,如果在iOS上需要使用
Swift
的話,在此勾選即可。
2.3 運作項目
至此,工程建立完畢。在
Terminal
視窗,運作
flutter run
即可将應用安裝到目前連接配接的手機上。
2.4 計數器
機智的你,也許發現,此時點選程式右下角的"➕",頁面中的數字會一直增加,簡單吧,不需要手寫一行代碼,一個計數器應用就完成了。
三、項目結構分析
Flutter
是采用
Dart
語言來編寫的,該示例項目中,主要
Dart
代碼在
lib/main.dart
中,接下來就對該檔案中的代碼進行分析。
3.1 導包
import 'package:flutter/material.dart';
該行代碼主要導入
Material UI
元件庫。
Material
是一種标準的移動端和
web
端的視覺設計語言,
Flutter
預設提供了一套豐富的
Material
風格的
UI
元件。
3.2 應用入口
void main() => runApp(MyApp());
Flutter
應用中,
main
函數是整個應用程式的入口。
main
函數中調用了
runApp
方法,其功能是啟動
Flutter
應用,接收一個
widget
參數,在該示例中,它是
MyApp
類的一個執行個體,該參數代表
Flutter
應用。
main函數使用了
=>
符号,這是
Dart
中單行函數或方法的簡寫。
3.3 應用結構
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
-
類代表MyApp
應用,它繼承了Flutter
類,也意味着應用本身也是一個StatelessWidget
widget
- 在
中,大多數東西都是Flutter
,包括對其(widget
)、填充(alignment
)和布局padding
)(layout
-
在建構頁面時,會調用組建的Flutter
方法,build
的主要工作是提供一個widget
方法來描述如何建構build()
界面,通常是通過組合、拼裝其他基礎UI
widget
-
是MaterialApp
庫提供的Material
架構,通過它可以設定應用的名稱、主題、首頁以及路由清單等,Flutter App
也是一個MaterialApp
widget
-
為home
應用的首頁,它也是一個Flutter
widget
3.4 首頁
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
...
}
MyHomePage
是應用的首頁,它繼承自
StatefulWidget
類,表示他是一個有狀态的
widget(Stateful widght)
,目前,可以簡單認為
Stateful Widget
和
Stateless Widget
有兩點不同:
a.
Stateful widget
可以擁有狀态,這些狀态在widget生命周期中時可以變的,而
Stateless widget
是不可變的
b.
Stateful widget
至少由兩個類組成:
- 一個
類StatefulWidget
- 一個
類,State
類本身是不可變的,但State類中持有的狀态在StatefulWidget
生命周期中可能會發生變化widget
_MyHomePageState
類時
MyHomePage
類對應的狀态類。看到這裡,也許機智的你已經發現,和
MyApp
類不同,
MyHomePage
類中沒有
build
方法,取而代之的是,
build
方法被挪到了
_MyHomePageState
方法中,至于為什麼這麼做,先留個疑問,在分析完完整代碼後再來解答。
3.5 _MyHomePageState詳解
接下來,一起看看
_MyHomePageState
中都包含哪些東西。
- 狀态
int _counter = 0;
為儲存螢幕右下角"➕"号按鈕點選次數的狀态。_counter
- 設定狀态的自增函數
當按鈕點選時,會調用此函數,該函數的作用是先自增void _incrementCounter() { setState(() { _counter++; }); }
,然後調用_counter
方法。setState
方法的作用是通知setState
架構,有狀态發生了變化,Flutter
架構收到通知後,會執行Flutter
方法來根據新的狀态重新建構界面,build
對此方法做了優化,使重新執行變得很快,是以你可以重新建構任何需要更新的東西,而無須分别去修改各個Flutter
。widget
- 建構
UI
界面
建構
界面的邏輯在UI
方法中,當build
第一次建立時,MyHomePage
類會被建立,當初始化完成後,_MyHomePageState
架構會調用Flutter
的Widget
方法來建構build
樹,最終将widget
樹渲染到裝置螢幕上,是以,來看一下widget
的_MyhomePageState
方法都幹了什麼事。build
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'You have pushed the button this many times:',
),
new Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: new FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: new Icon(Icons.add),
),
);
}
-
是Scaffold
庫中提供的頁面腳手架,提供了預設的導航欄、标題和包含主螢幕Material
樹的widget
屬性,body
樹可以很複雜widget
-
的body
樹中包含了一個widget
,Center widget
可以将其子Center
樹對齊到螢幕中心,widget
子Center
是一個widget
,Column widget
的作用是将其所有字Column
沿螢幕垂直方向依次排列,此例中widget
包含兩個Column
子Text
,第一個widget
顯示固定為文本Text widget
,第二個You have pushed the button this many times:
顯示Text widget
_counter
狀态的數值
-
是頁面右下角的帶"➕"的炫富按鈕,它的floatingActionButton
屬性接受一個毀掉參數,代表它被點選後的處理事件,本例中将onPressed
作為其處理函數_incrementCounter
3.6 流程小結
現在,我們将整個流程串起來:當右下角的
floatingActionButton
按鈕被點選後,會調用
_incrementCounter
,在
_incrementCounter
中,首先會自增
_counter
計數器(狀态),然後
setState
會通知
Flutter
架構狀态發生變化, 接着,
Flutter
會調用
build
方法以新的狀态重新建構
UI
,最終顯示在裝置螢幕上。
四、解惑build為何放在State中
現在,我們可以回答之前的問題,為什麼
build
方法在
State
,而不是
StatefulWidget
中?
這個主要是為了靈活性,如果将
build
方法放在
StatefulWidget
中則會有兩個問題:
-
狀态通路不便
試想一下,如果我們的
有很多狀态,而每次狀态改變都要調用Stateful widget
方法,由于狀态是儲存在build
中的,如果将State
方法放在build
中,那麼建構時讀取狀态會很不友善。如果真的将StatefulWidget
方法放在build
中的話,由于建構使用者界面過程需要依賴StatefulWidget
,是以State
方法必須加一個build
參數,大概是下面這樣:State
Widget build(BuildContext context, State state){
//state.counter
...
}
這樣的話,就隻能将
State
的所有狀态聲明為公開的狀态,這樣才能在
State
類外部通路狀态,但将狀态設定為公開後,狀态将不再具有私密性,這樣依賴,對狀态的修改将會變得不可控。但将
build
方法放入
State
中的話,建構過程可以直接通路狀态,會很友善。
- 繼承
StatefulWidget
不便
例如,
中有一個動畫Flutter
的基類widget
,它繼承自AnimatedWidget
。StatefulWidget
中引入了一個抽象方法AnimatedWidget
,繼承自build(BuildContext context)
widgetAnimatedWidget的動畫
build都要實作這個
StatefulWidget方法。 現在試想一下,如果
build類中已經有了一個
AnimatedWidget方法,正如上面所述,此時build方法需要接受一個state對象,這意味着
State必須先将自己的
_animatedWidgetState對象(記為
build)提供給其自雷,因為子類需要在其
build`方法,代碼可能如下:方法中調用父類的
class MyAnimationWidget extends AnimatedWidget{
@override
Widget build(BuildContext context, State state){
//由于子類要用到AnimatedWidget的狀态對象_animatedWidgetState,
//是以AnimatedWidget必須通過某種方式将其狀态對象_animatedWidgetState
//暴露給其子類
super.build(context, _animatedWidgetState)
}
}
這樣顯然是不合理的,因為:
-
的狀态對象是AnimatedWidget
内部實作細節,不應該暴露給外部AnimatedWidget
- 如果要将父類狀态暴露給子類,那麼必須有一種傳遞機制,二做這一套傳遞機制是毫無意義的,因為父子類之間狀态的傳遞和子類本身邏輯是無關的。
綜上所述,對于
StatefulWidget
,将
build
方法放在
State
中,可以給開發帶來很大的靈活性,更加規範。