一、簡介
Flutter Widget採用現代響應式框架構建,核心思想是用widget
元件來構建UI,當widget
的狀態發生變化時,widget
也會對應重新構建UI,Flutrer會對比前後變化的不同,以確定底層渲染樹從一個狀態轉換到下一個狀態所需的最小更改。
二、入口程式
通過flutter create
建立的專案,我們可以看到在lib
目錄下有一個預設的mian.dart
檔案,下面來看下這個檔案裡的內容。
Flutter跟Java程式一樣,程式要跑起來就必須要一個入口,Java中用的是main方法當作入口,Flutter也是一樣
void main() => runApp(MyApp());
複製程式碼
這個main
就是flutter
的入口,使用runApp
函式可以將給定的根元件填滿整個螢幕。
三、基礎的widget
3.1 無狀態和有狀態的元件
接著看flutter create
建立出來的這個專案程式碼:
class MyApp extends StatelessWidget {}
class MyHomePage extends StatefulWidget {}
複製程式碼
-
StatelessWidget表示無狀態的元件,這個意味著它裡面的值都是不可變的
-
StatefulWidget表示有狀態的元件,這個意味著
StatefulWidget
持有的狀態在widget的生命週期中發生改變,建立一個StatefulWidget需要兩個類:class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context){ return Container(); } } 複製程式碼
StatefulWidget類本身是不變的,但State類在Widget中的生命週期是始終存在的
3.2 常用的基礎Widget
Text
:帶格式的文字。相當於Android中的TextView
Row
、Column
:水平和垂直方向的佈局元件。符合Vue中的Flexbox佈局模型,相當於Android中的LinearLayout的orientation=horizontal和vertical
Stack
:允許子widget堆疊,可以用Positioned
來定位上下左右的位置。相當於Android中的RelativeLayout
Container
:建立一個矩形視覺元素,可以通過BoxDecoration
設定background、邊框、陰影。相當於C#中的panel
四、Material元件
幾乎所有的dart檔案中都會引入flutter/material.dart
這個包,這個是一個實現了Material Design
風格的元件庫,如flutter create
建立出來的這個專案程式碼中的MaterialApp
、Scaffold
、Navigator
等元件
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'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
複製程式碼
五、State的生命週期
Flutter程式可以看成是一個巨大的狀態機器,使用者的操作、網路請求、系統事件都是推動這個狀態機執行的觸發點,觸發點通過setState
來推動狀態機的改變。
建構函式
-->initState
-->didChangeDepencies
-->build
-->deactivate
-->dispose
- 建構函式:呼叫一次
- initState:呼叫一次,在這裡可以做一些初始化的工作,比如變數初始化、呼叫網路介面。相當於Android中的
onCreate
- didChangeDependencies:多次呼叫,在
initState
之後立刻呼叫,在InheritedWidget rebuild
時也會觸發呼叫 - build:呼叫多次,第一次繪製介面時呼叫,當setState時也會被觸發呼叫
- didUpdateWidget:元件狀態改變時呼叫,可能會呼叫多次,在widget重新構建時,Flutter framework會呼叫
Widget.canUpdate
來檢測Widget樹中同一位置的新舊節點,然後決定是否需要更新,如果Widget.canUpdate
返回true
則會呼叫此回撥。正如之前所述,Widget.canUpdate
會在新舊widget的key和runtimeType同時相等時會返回true,也就是說在在新舊widget的key和runtimeType同時相等時didUpdateWidget()
就會被呼叫。如rebuild、hot reload - deactivate:呼叫多次,當移除渲染樹時呼叫,比如跳轉新介面、棧頂介面被銷燬時都會呼叫。相當於Android中的
onDetatch
和onResume
- dispose:呼叫一次,元件即將銷燬時呼叫。相當於Android中的
onDestroy
看下面的程式碼例子:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primaryColor: Colors.white,
primarySwatch: Colors.blue,
),
home: WidgetLifePage(),
);
}
}
class WidgetLifePage extends StatefulWidget {
@override
_WidgetLifePageState createState() => _WidgetLifePageState();
}
class _WidgetLifePageState extends State<WidgetLifePage> {
int _page1Count = 0;
Color _color = Colors.black;
_increase() {
setState(() {
_page1Count++;
});
_changeColor();
}
_changeColor() {
setState(() {
_color == Colors.black ? _color = Colors.blue : _color = Colors.black;
});
}
@override
Widget build(BuildContext context) {
print("page1 build");
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"widget life page 1 count = $_page1Count",
style: TextStyle(color: _color),
),
RaisedButton(
onPressed: () => _increase(),
child: Text("增加"),
),
RaisedButton(
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => WidgetLifePage2()));
},
child: Text("跳轉"),
),
],
),
),
);
}
@override
void initState() {
print("page1 initState");
super.initState();
}
@override
void didChangeDependencies() {
print("page1 didChangeDependencies");
super.didChangeDependencies();
}
@override
void didUpdateWidget(WidgetLifePage oldWidget) {
print("page1 didUpdateWidget");
super.didUpdateWidget(oldWidget);
}
@override
void deactivate() {
print("page1 deactivate");
super.deactivate();
}
@override
void dispose() {
print("page1 dispose");
super.dispose();
}
}
class WidgetLifePage2 extends StatefulWidget {
@override
_WidgetLifePage2State createState() => _WidgetLifePage2State();
}
class _WidgetLifePage2State extends State<WidgetLifePage2> {
int _page2Count = 0;
_increase() {
setState(() {
_page2Count++;
});
}
@override
Widget build(BuildContext context) {
print("page2 build");
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("widget life page 2 count = $_page2Count"),
RaisedButton(
onPressed: () => _increase(),
child: Text("增加"),
),
RaisedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("返回"),
),
],
),
),
);
}
@override
void initState() {
print("page2 initState");
super.initState();
}
@override
void didChangeDependencies() {
print("page2 didChangeDependencies");
super.didChangeDependencies();
}
@override
void didUpdateWidget(WidgetLifePage2 oldWidget) {
print("page2 didUpdateWidget");
super.didUpdateWidget(oldWidget);
}
@override
void deactivate() {
print("page2 deactivate");
super.deactivate();
}
@override
void dispose() {
print("page2 dispose");
super.dispose();
}
}
複製程式碼
我們想下這個print
列印結果的先後順序是什麼?
-
WidgetLifePage和WidgetLifePage2啟動時分別呼叫了哪些生命週期的方法?
//WidgetLifePage啟動時 page1 initState page1 didChangeDependencies page1 build //WidgetLifePage2啟動時 page2 initState page2 didChangeDependencies page2 build 複製程式碼
-
WidgetLifePage跳轉WidgetLifePage2時分別呼叫了哪些生命週期?
page2 initState page2 didChangeDependencies page2 build page1 deactivate page1 build 複製程式碼
-
WidgetLifePage2關閉時WidgetLifePage和WidgetLifePage2分別呼叫了哪些生命週期?
page1 deactivate page1 build page2 deactivate page2 dispose 複製程式碼
-
setState
執行時有哪些生命週期方法被觸發了?setState會導致Widget重新繪製,也就是build方法被觸發。