用Android Studio和VS Code建立的Flutter應用,源碼模板是一個計數器示例,經過講解分析計數器示例的源碼,可讓讀者對Flutter應用程序結構有個最基本的瞭解,在後續的文章中,筆者將會基於此示例,一步一步添加新的功能來介紹Flutter應用的其餘概念與技術。web
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
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> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
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),
),
);
}
}
複製代碼
import 'package:flutter/material.dart';
複製代碼
void main() => runApp(MyApp());
複製代碼
main
函數爲應用程序的入口,main
函數中調用了runApp
方法,它的功能是啓動Flutter應用,它接受一個Widget
參數,在本示例中它是MyApp
類的一個實例,該參數表明Flutter應用。main
函數使用了(=>
)符號,這是Dart中單行函數或方法的簡寫。class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
//應用名稱
title: 'Flutter Demo',
//應用主題
theme: new ThemeData(
//藍色主題
primarySwatch: Colors.blue,
),
//應用首頁路由
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
複製代碼
MyApp
類表明Flutter應用,它繼承了StatelessWidget
類,這意味着應用自己也是一個Widget。在Flutter中,一切皆爲組件(Widget),包括對齊(alignment)、填充(padding)和佈局(layout)。build
方法,Widget的主要工做是提供一個build
方法來描述如何構建UI界面(一般都是經過組合、拼裝其餘基礎Widget)。MaterialApp
是Material庫中提供的Flutter APP框架,經過它能夠設置應用的名稱、主題、語言和首頁及路由列表等。MaterialApp
也是一個Widget。home
爲Flutter應用的首頁,它也是一個widget。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 widget),那麼Stateful widget和Stateless widget有什麼區別呢?(1)、Stateful widget能夠擁有狀態,這些狀態在Widget生命週期中是能夠變的,而Stateless widget是不可變的。bash
(2)、Stateful widget至少由兩個類組成:app
①、一個
StatefulWidget
類;框架②、一個
State
類,StatefulWidget
類自己是不變的,可是State
類中持有的狀態在Widget生命週期中可能會發生變化。less
_MyHomePageState
類是MyHomePage
類對應的狀態類。看到這裏,細心的讀者可能已經發現,和MyApp
類不一樣的是,MyHomePage
類中並無build
方法,取而代之的是,build
方法被挪到了_MyHomePageState
方法中,至於爲何這麼作,先留個疑問,在分析完完整代碼後再來解答,接下來,咱們看看_MyHomePageState
中都包含哪些東西:
狀態ide
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,它提供了默認的導航欄、標題和包含主屏幕widget樹的body屬性。路由默認都是經過Scaffold
建立。
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
做爲其處理函數。
如今,咱們將整個流程串起來:當右下角的floatingActionButton按鈕被點擊以後,會調用_incrementCounter
函數,在_incrementCounter
函數中,首先會自增_counter
狀態(計數器),而後setState
會通知Flutter框架狀態發生變化,接着Flutter會調用build
方法以新的狀態從新構建UI,最終顯示在設備屏幕上。
如今,咱們回答以前提出的問題,爲何build
方法在State中,而不是StatefulWidget中?這主要是爲了開發的靈活性。若是將build
方法在StatefulWidget中則會有兩個問題:
狀態訪問不便
試想一下,若是咱們的Stateful widget有不少狀態,而每次狀態改變都要調用build
方法。因爲狀態是保存在State中的,若是將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)
,繼承自AnimatedWidget
的動畫Widget都要實現這個build
方法。試想一下,若是StatefulWidget類中已經有了一個build
方法,正如上述所述,此時build
方法須要接收一個State對象,這就意味着AnimatedWidget
必須將本身的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
中,能夠給開發帶來很大的靈活性。