目錄
0 引言
1 Widget 簡(jiǎn)介
1.1 Widget 概念
1.2?Widget 接口
1.3?Flutter中的四棵樹(shù)
1.4?StatelessWidget
1.4.1 簡(jiǎn)介
1.4.2?Context上下文
1.5?StatefulWidget
1.6??State
1.6.1 簡(jiǎn)介
1.6.2?State生命周期
1.7??在 widget 樹(shù)中獲取State對(duì)象
1.7.1 通過(guò)Context獲取
1.7.2 通過(guò)GlobalKey獲取
1.8?通過(guò) RenderObject 自定義 Widget
1.9?Flutter SDK內(nèi)置組件庫(kù)介紹
1.9.1?基礎(chǔ)組件
1.9.2 Material組件
1.9.3 ?Cupertino組件
1.10 總結(jié)
0 引言
本文是對(duì)第二版序 | 《Flutter實(shí)戰(zhàn)·第二版》 (flutterchina.club)的學(xué)習(xí)和總結(jié)。
1 Widget 簡(jiǎn)介
1.1 Widget 概念
- Widget 字面意思:控件、組件、部件、微件、插件、小工具
- widget 的功能是“描述一個(gè)UI元素的配置信息”,所謂的配置信息就是 Widget 接收的參數(shù)
- Widget 只是描述一個(gè)UI元素的配置信息,并不是表示最終繪制在設(shè)備屏幕上的顯示元素
1.2?Widget 接口
@immutable
?代表 Widget 是不可變的,即Widget 中定義的屬性必須是 final。widget
類(lèi)繼承自DiagnosticableTree
,即“診斷樹(shù)”,主要作用是提供調(diào)試信息。Key
屬性類(lèi)似于 React/Vue 中的key
,主要的作用是決定是否在下一次build
時(shí)復(fù)用舊的 widget ,決定的條件在canUpdate()
方法中。createElement():
一個(gè) widget 可以對(duì)應(yīng)多個(gè)Element,
Flutter 框架在構(gòu)建UI樹(shù)時(shí),會(huì)先調(diào)用此方法生成對(duì)應(yīng)節(jié)點(diǎn)的Element
對(duì)象。此方法是 Flutter 框架隱式調(diào)用的,在開(kāi)發(fā)過(guò)程中基本不會(huì)調(diào)用到。debugFillProperties(...)
?復(fù)寫(xiě)父類(lèi)的方法,主要是設(shè)置診斷樹(shù)的一些特性。canUpdate(...)
是一個(gè)靜態(tài)方法,只要newWidget
與oldWidget
的runtimeType
和key
同時(shí)相等,就會(huì)用new widget
去更新舊UI樹(shù)上所對(duì)應(yīng)的Element
對(duì)象的配置,否則會(huì)創(chuàng)建新的Element對(duì)象
。- 在 Flutter 開(kāi)發(fā)中,一般不用直接繼承
Widget
類(lèi)來(lái)實(shí)現(xiàn)一個(gè)新組件。通常會(huì)通過(guò)繼承StatelessWidget
或StatefulWidget
來(lái)間接繼承widget
類(lèi)來(lái)實(shí)現(xiàn)。
/*
Widget 類(lèi)的聲明:
*/
@immutable // 不可變的
abstract class Widget extends DiagnosticableTree {
const Widget({ this.key });
final Key? key;
@protected
@factory
Element createElement();
@override
String toStringShort() {
final String type = objectRuntimeType(this, 'Widget');
return key == null ? type : '$type-$key';
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
}
@override
@nonVirtual
bool operator ==(Object other) => super == other;
@override
@nonVirtual
int get hashCode => super.hashCode;
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
...
}
1.3?Flutter中的四棵樹(shù)
Flutter 框架的處理流程:(真正的布局、繪制是由誰(shuí)來(lái)完成的)
- 根據(jù) Widget 樹(shù)生成一個(gè) Element 樹(shù),Element 樹(shù)中的節(jié)點(diǎn)都繼承自?
Element
?類(lèi)。- 根據(jù) Element 樹(shù)生成 Render 樹(shù)(渲染樹(shù)),渲染樹(shù)中的節(jié)點(diǎn)都繼承自
RenderObject
?類(lèi)。- 根據(jù)渲染樹(shù)生成 Layer 樹(shù),然后上屏顯示,Layer 樹(shù)中的節(jié)點(diǎn)都繼承自?
Layer
?類(lèi)。
- ?Flutter 真正的布局和渲染邏輯在 Render 樹(shù)中
- Element 是 Widget 和 RenderObject 的粘合劑,可以理解為一個(gè)中間代理。
/*假設(shè)有如下 Widget 樹(shù):
Container 內(nèi)部會(huì)創(chuàng)建一個(gè)新的 ColoredBox 來(lái)填充背景
Image 內(nèi)部會(huì)通過(guò) RawImage 來(lái)渲染圖片
Text 內(nèi)部會(huì)通過(guò) RichText 來(lái)渲染文本
*/
Container( // 一個(gè)容器 widget
color: Colors.blue, // 設(shè)置容器背景色
child: Row( // 可以將子widget沿水平方向排列
children: [
Image.network('https://www.example.com/1.png'), // 顯示圖片的 widget
const Text('A'),
],
),
);
?Widget樹(shù)、Element 樹(shù)、渲染樹(shù)結(jié)構(gòu):
1.4?StatelessWidget
1.4.1 簡(jiǎn)介
- 無(wú)狀態(tài)的組件,用于不需要維護(hù)狀態(tài)的場(chǎng)景
- 繼承自
widget
類(lèi),重寫(xiě)了createElement()
方法- 通常在
build
方法中通過(guò)嵌套其他 widget 來(lái)構(gòu)建UI,在構(gòu)建過(guò)程中會(huì)遞歸的構(gòu)建其嵌套的 widget
//main 函數(shù)為應(yīng)用程序的入口
void main() {
//runApp(Widget參數(shù)),它的功能是啟動(dòng)Flutter應(yīng)用
runApp(const MyApp());
}
//MyApp對(duì)象
class MyApp extends StatelessWidget {
const MyApp({super.key});
// 應(yīng)用的根組件
@override
Widget build(BuildContext context) {
// 調(diào)用自定義的 widget
return Echo(text: "hello world");
}
}
// 自定義 widget
class Echo extends StatelessWidget {
//使用命名參數(shù),定義 widget 的構(gòu)造函數(shù)
const Echo({
Key? key, //在繼承 widget 時(shí),第一個(gè)參數(shù)通常應(yīng)該是Key
required this.text, //必需要傳的參數(shù)要添加required關(guān)鍵字
this.backgroundColor = Colors.grey, //默認(rèn)為灰色
}):super(key:key);
// widget 的屬性應(yīng)盡可能的被聲明為final,防止被意外改變
final String text;
final Color backgroundColor;
@override
Widget build(BuildContext context) {
return Center(
child: Container(
color: backgroundColor,
child: Text(text), //child或children參數(shù)通常應(yīng)被放在參數(shù)列表的最后
),
);
}
1.4.2?Context上下文
- ?
build
方法有一個(gè)context
參數(shù)- 它是
BuildContext
類(lèi)的一個(gè)實(shí)例,表示當(dāng)前 widget 在 widget 樹(shù)中的上下文- 每一個(gè) widget 都會(huì)對(duì)應(yīng)一個(gè) context 對(duì)象
- 它提供了從當(dāng)前 widget 開(kāi)始向上遍歷 widget 樹(shù)的方法
- 它提供了按照 widget 類(lèi)型查找父級(jí) widget 的方法
class ContextRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
/*
Scaffold 是 Material 庫(kù)中提供的頁(yè)面腳手架
它提供了默認(rèn)的導(dǎo)航欄、標(biāo)題和包含主屏幕 widget 樹(shù)的body屬性
*/
return Scaffold(
appBar: AppBar(
title: Text("Context測(cè)試"),
),
body: Container( // 一個(gè)容器 widget
child: Builder(builder: (context) {
// 在 widget 樹(shù)中向上查找最近的父級(jí)`Scaffold` widget
Scaffold scaffold = context.findAncestorWidgetOfExactType<Scaffold>();
// 直接返回 AppBar的title, 此處實(shí)際上是Text("Context測(cè)試")
return (scaffold.appBar as AppBar).title;
}),
),
);
}
}
1.5?StatefulWidget
- 有狀態(tài)的組件,用于需要維護(hù)狀態(tài)的場(chǎng)景
- 繼承自
widget
類(lèi),重寫(xiě)了createElement()
方法- 添加了一個(gè)新的接口
createState()
- State 對(duì)象和
StatefulElement
具有一一對(duì)應(yīng)的關(guān)系
/*
StatefulWidget的類(lèi)定義:
*/
abstract class StatefulWidget extends Widget {
const StatefulWidget({ Key key }) : super(key: key);
@override
StatefulElement createElement() => StatefulElement(this);
/*
用于創(chuàng)建和 StatefulWidget 相關(guān)的狀態(tài),
在StatefulWidget 的生命周期中可能會(huì)被多次調(diào)用,
一個(gè)StatefulElement對(duì)應(yīng)一個(gè)State實(shí)例
*/
@protected
State createState();
}
1.6??State
1.6.1 簡(jiǎn)介
一個(gè) StatefulWidget 類(lèi)會(huì)對(duì)應(yīng)一個(gè) State 類(lèi),State表示與其對(duì)應(yīng)的 StatefulWidget 要維護(hù)的狀態(tài),State 中保存的狀態(tài)信息可以:
- 在 widget 構(gòu)建時(shí)可以被同步讀取。
- 在 widget 生命周期中可以被改變,當(dāng)State被改變時(shí),可以手動(dòng)調(diào)用其
setState()
方法通知Flutter 框架狀態(tài)發(fā)生改變,F(xiàn)lutter 框架在收到消息后,會(huì)重新調(diào)用其build
方法重新構(gòu)建 widget 樹(shù),從而達(dá)到更新UI的目的。
?State 中有兩個(gè)常用屬性:
widget
,它表示與該 State 實(shí)例關(guān)聯(lián)的 widget 實(shí)例,由Flutter 框架動(dòng)態(tài)設(shè)置。注意,這種關(guān)聯(lián)并非永久的,因?yàn)樵趹?yīng)用生命周期中,UI樹(shù)上的某一個(gè)節(jié)點(diǎn)的 widget 實(shí)例在重新構(gòu)建時(shí)可能會(huì)變化,但State實(shí)例只會(huì)在第一次插入到樹(shù)中時(shí)被創(chuàng)建,當(dāng)在重新構(gòu)建時(shí),如果 widget 被修改了,F(xiàn)lutter 框架會(huì)動(dòng)態(tài)設(shè)置State. widget 為新的 widget 實(shí)例。context
。StatefulWidget對(duì)應(yīng)的 BuildContext,作用同StatelessWidget 的BuildContext。
1.6.2?State生命周期
initState
:當(dāng) widget 第一次插入到 widget 樹(shù)時(shí)會(huì)被調(diào)用,對(duì)于每一個(gè)State對(duì)象,F(xiàn)lutter 框架只會(huì)調(diào)用一次該回調(diào)。所以,通常在該回調(diào)中做一些一次性的操作,如狀態(tài)初始化、訂閱子樹(shù)的事件通知等。didChangeDependencies()
:當(dāng)State對(duì)象的依賴(lài)發(fā)生變化時(shí)會(huì)被調(diào)用。組件第一次被創(chuàng)建后掛載的時(shí)候(包括重創(chuàng)建)對(duì)應(yīng)的didChangeDependencies
也會(huì)被調(diào)用。
build()
:用于構(gòu)建 widget 子樹(shù)的,會(huì)在如下場(chǎng)景被調(diào)用:
- 在調(diào)用
initState()
之后。- 在調(diào)用
didUpdateWidget()
之后。- 在調(diào)用
setState()
之后。- 在調(diào)用
didChangeDependencies()
之后。- 在State對(duì)象從樹(shù)中一個(gè)位置移除后(會(huì)調(diào)用deactivate)又重新插入到樹(shù)的其他位置之后。
reassemble()
:專(zhuān)門(mén)為了開(kāi)發(fā)調(diào)試而提供的,在熱重載(hot reload)時(shí)會(huì)被調(diào)用,在Release模式下永遠(yuǎn)不會(huì)被調(diào)用。
didUpdateWidget()
:在 widget 重新構(gòu)建時(shí),F(xiàn)lutter 框架會(huì)調(diào)用widget.canUpdate
來(lái)檢測(cè) widget 樹(shù)中同一位置的新舊節(jié)點(diǎn),然后決定是否需要更新,如果widget.canUpdate
返回true
則會(huì)調(diào)用此回調(diào)。widget.canUpdate
會(huì)在新舊 widget 的?key
?和?runtimeType
?同時(shí)相等時(shí)會(huì)返回true。
deactivate()
:當(dāng) State 對(duì)象從樹(shù)中被移除時(shí),會(huì)調(diào)用此回調(diào)。在一些場(chǎng)景下,F(xiàn)lutter 框架會(huì)將 State 對(duì)象重新插到樹(shù)中,如包含此 State 對(duì)象的子樹(shù)在樹(shù)的一個(gè)位置移動(dòng)到另一個(gè)位置時(shí)。如果移除后沒(méi)有重新插入到樹(shù)中則緊接著會(huì)調(diào)用dispose()
方法。
dispose()
:當(dāng) State 對(duì)象從樹(shù)中被永久移除時(shí)調(diào)用;通常在此回調(diào)中釋放資源。注意:在繼承
StatefulWidget
重寫(xiě)其方法時(shí),對(duì)于包含@mustCallSuper
標(biāo)注的父類(lèi)方法,都要在子類(lèi)方法中調(diào)用父類(lèi)方法。
/*
實(shí)現(xiàn)一個(gè)計(jì)數(shù)器 CounterWidget 組件 ,點(diǎn)擊它可以使計(jì)數(shù)器加1,
由于要保存計(jì)數(shù)器的數(shù)值狀態(tài),所以應(yīng)繼承StatefulWidget
*/
class CounterWidget extends StatefulWidget {
//構(gòu)造函數(shù)
const CounterWidget({Key? key, this.initValue = 0});
//組件的參數(shù)
final int initValue;
//重寫(xiě)createState方法
@override
_CounterWidgetState createState() => _CounterWidgetState();
}
/*
createState方法
*/
class _CounterWidgetState extends State<CounterWidget> {
int _counter = 0;
@override
void initState() {
super.initState();
//初始化狀態(tài)
_counter = widget.initValue;
print("initState");
}
@override
Widget build(BuildContext context) {
print("build");
return Scaffold(
body: Center(
child: TextButton(
child: Text('$_counter'),
//點(diǎn)擊后計(jì)數(shù)器自增
onPressed: () => setState(
() => ++_counter,
),
),
),
);
}
@override
void didUpdateWidget(CounterWidget oldWidget) {
super.didUpdateWidget(oldWidget);
print("didUpdateWidget ");
}
@override
void deactivate() {
super.deactivate();
print("deactivate");
}
@override
void dispose() {
super.dispose();
print("dispose");
}
@override
void reassemble() {
super.reassemble();
print("reassemble");
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("didChangeDependencies");
}
}
/*
調(diào)用自定義的CounterWidget
*/
class StateLifecycleTest extends StatelessWidget {
const StateLifecycleTest({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return CounterWidget();
}
}
/*
1.運(yùn)行應(yīng)用,屏幕中央就會(huì)出現(xiàn)一個(gè)數(shù)字0,然后控制臺(tái)日志輸出:
I/flutter ( 5436): initState
I/flutter ( 5436): didChangeDependencies
I/flutter ( 5436): build
2.熱重載,控制臺(tái)輸出日志如下:
I/flutter ( 5436): reassemble
I/flutter ( 5436): didUpdateWidget
I/flutter ( 5436): build
3.將return CounterWidget();改為 return Text("xxx");然后熱重載,日志如下:
I/flutter ( 5436): reassemble
I/flutter ( 5436): deactive
I/flutter ( 5436): dispose
*/
?StatefulWidget 生命周期如下圖所示:
1.7??在 widget 樹(shù)中獲取State對(duì)象
在子 widget 樹(shù)中獲取父級(jí) StatefulWidget 的State 對(duì)象的兩種方法:
- 通過(guò)Context獲?。?code>context對(duì)象有一個(gè)
findAncestorStateOfType()
方法,該方法可以從當(dāng)前節(jié)點(diǎn)沿著 widget 樹(shù)向上查找指定類(lèi)型的 StatefulWidget 對(duì)應(yīng)的 State 對(duì)象。- 通過(guò)GlobalKey獲取:GlobalKey 是 Flutter 提供的一種在整個(gè) App 中引用 element 的機(jī)制。如果一個(gè) widget 設(shè)置了
GlobalKey,
則可以通過(guò)globalKey.currentState
來(lái)獲得該 widget 對(duì)應(yīng)的state對(duì)象。(開(kāi)銷(xiāo)大,不推薦這種方法)
1.7.1 通過(guò)Context獲取
通過(guò)Context獲取State 對(duì)象的兩種方法:
- 通過(guò)?
context.findAncestorStateOfType
?獲取 StatefulWidget 的狀態(tài)的方法是通用的。- 但如果 StatefulWidget 的狀態(tài)是公共的(希望暴露出的),F(xiàn)lutter SDK默認(rèn)在 StatefulWidget 中提供一個(gè)
of
?靜態(tài)方法來(lái)供開(kāi)發(fā)者獲取其 State 對(duì)象。開(kāi)發(fā)者在自定義StatefulWidget 時(shí)也應(yīng)遵守這一規(guī)則。
// Scaffold組件對(duì)應(yīng)的狀態(tài)類(lèi)ScaffoldState中定義了打開(kāi) SnackBar(路由頁(yè)底部提示條)的方法
// 下面是實(shí)現(xiàn)打開(kāi) SnackBar 的示例:
/*
1.通過(guò)context.findAncestorStateOfType獲取ScaffoldState
*/
class GetStateObjectRoute extends StatefulWidget {
const GetStateObjectRoute({Key? key}) : super(key: key);
@override
State<GetStateObjectRoute> createState() => _GetStateObjectRouteState();
}
class _GetStateObjectRouteState extends State<GetStateObjectRoute> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("子樹(shù)中獲取State對(duì)象"),
),
body: Center(
child: Column(
children: [
Builder(builder: (context) {
return ElevatedButton(
onPressed: () {
// 查找父級(jí)最近的Scaffold對(duì)應(yīng)的ScaffoldState對(duì)象
ScaffoldState _state = context.findAncestorStateOfType<ScaffoldState>()!;
// 打開(kāi)抽屜菜單
_state.openDrawer();
},
child: Text('打開(kāi)抽屜菜單1'),
);
}),
],
),
),
drawer: Drawer(),
);
}
}
/*
2.通過(guò)of靜態(tài)方法來(lái)獲取ScaffoldState
*/
Builder(builder: (context) {
return ElevatedButton(
onPressed: () {
// 直接通過(guò)of靜態(tài)方法來(lái)獲取ScaffoldState
ScaffoldState _state=Scaffold.of(context);
// 打開(kāi)抽屜菜單
_state.openDrawer();
},
child: Text('打開(kāi)抽屜菜單2'),
);
}),
/*
顯示 snack bar 的代碼
*/
Builder(builder: (context) {
return ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("我是SnackBar")),
);
},
child: Text('顯示SnackBar'),
);
}),
1.7.2 通過(guò)GlobalKey獲取
如果一個(gè) widget 設(shè)置了
GlobalKey,則:
- 可以通過(guò)
globalKey.currentWidget
獲得該 widget 對(duì)象- 可以通過(guò)
globalKey.currentElement
來(lái)獲得 widget 對(duì)應(yīng)的element對(duì)象- 可以通過(guò)
globalKey.currentState
來(lái)獲得該 widget 對(duì)應(yīng)的state對(duì)象
注意事項(xiàng):
- 使用 GlobalKey 開(kāi)銷(xiāo)較大,如果有其他可選方案,應(yīng)盡量避免使用它。
- 同一個(gè) GlobalKey 在整個(gè) widget 樹(shù)中必須是唯一的,不能重復(fù)。?
/*
Flutter還有一種通用的獲取State對(duì)象的方法——通過(guò)GlobalKey來(lái)獲??! 步驟分兩步:
*/
/*
1.給目標(biāo)StatefulWidget添加GlobalKey。
*/
//定義一個(gè)globalKey, 由于GlobalKey要保持全局唯一性,我們使用靜態(tài)變量存儲(chǔ)
static GlobalKey<ScaffoldState> _globalKey= GlobalKey();
...
Scaffold(
key: _globalKey , //設(shè)置key
...
)
/*
2.通過(guò)GlobalKey來(lái)獲取State對(duì)象
*/
_globalKey.currentState.openDrawer()
1.8?通過(guò) RenderObject 自定義 Widget
- Flutter 最原始的定義組件的方式是通過(guò)定義RenderObject 來(lái)實(shí)現(xiàn)的。
StatelessWidget
?和?StatefulWidget
?本身沒(méi)有對(duì)應(yīng)的 RenderObject,它們是用于組合其他組件的,F(xiàn)lutter 組件庫(kù)中的很多基礎(chǔ)組件都不是通過(guò)它們來(lái)實(shí)現(xiàn)的。- 就好比搭積木,
StatelessWidget 和
?StatefulWidget
?可以將積木搭成不同的樣子,但前提是得有積木,而這些積木都是通過(guò)自定義 RenderObject 來(lái)實(shí)現(xiàn)的,比如 Text 、Column、Align等。
通過(guò)RenderObject定義組件的方式:
- 如果自定義的 widget 不會(huì)包含子組件,可以直接繼承LeafRenderObjectWidget
- 如果自定義的 widget 可以包含子組件,則可以根據(jù)子組件的數(shù)量來(lái)選擇繼承SingleChildRenderObjectWidget 或 MultiChildRenderObjectWidget
- 它們都繼承自RenderObjectWidget
- 它們都實(shí)現(xiàn)了createElement() 方法,返回不同類(lèi)型的 Element 對(duì)象
- createRenderObject 方法被組件對(duì)應(yīng)的 Element 調(diào)用(構(gòu)建渲染樹(shù)時(shí))用于生成渲染對(duì)象。
- updateRenderObject 方法是用于在組件樹(shù)狀態(tài)發(fā)生變化但不需要重新創(chuàng)建 RenderObject 時(shí)用于更新組件渲染對(duì)象的回調(diào)。
/*
自定義Widget 繼承 葉渲染器Widget
*/
class CustomWidget extends LeafRenderObjectWidget{
@override
RenderObject createRenderObject(BuildContext context) {
// 創(chuàng)建 RenderObject
return RenderCustomObject();
}
@override
void updateRenderObject(BuildContext context, RenderCustomObject renderObject) {
// 更新 RenderObject
super.updateRenderObject(context, renderObject);
}
}
/*
自定義RenderObject,繼承自 RenderBox,RenderBox 繼承自 RenderObject,
需要在 RenderCustomObject 中實(shí)現(xiàn)布局、繪制、事件響應(yīng)等邏輯
*/
class RenderCustomObject extends RenderBox{
@override
void performLayout() {
// 實(shí)現(xiàn)布局邏輯
}
@override
void paint(PaintingContext context, Offset offset) {
// 實(shí)現(xiàn)繪制
}
}
/*
如果組件不會(huì)包含子組件,可以直接繼承自 LeafRenderObjectWidget ,
它是 RenderObjectWidget 的子類(lèi),而 RenderObjectWidget 繼承自 Widget,
LeafRenderObjectWidget 的實(shí)現(xiàn)如下:
*/
abstract class LeafRenderObjectWidget extends RenderObjectWidget {
const LeafRenderObjectWidget({ Key? key }) : super(key: key);
@override
LeafRenderObjectElement createElement() => LeafRenderObjectElement(this);
}
1.9?Flutter SDK內(nèi)置組件庫(kù)介紹
Flutter 提供了一套豐富、強(qiáng)大的基礎(chǔ)組件庫(kù),在基礎(chǔ)組件庫(kù)之上 Flutter 又提供了:
- 一套 Material 風(fēng)格( Android 默認(rèn)的視覺(jué)風(fēng)格)的組件庫(kù)
- 一套 Cupertino 風(fēng)格(iOS視覺(jué)風(fēng)格)的組件庫(kù)
1.9.1?基礎(chǔ)組件
?要使用基礎(chǔ)組件,需要先導(dǎo)入:
import 'package:flutter/widgets.dart';
常用的基礎(chǔ)組件:
- Text?(opens new window):該組件可讓您創(chuàng)建一個(gè)帶格式的文本。
- Row?(opens new window)、?Column?(opens new window): 這些具有彈性空間的布局類(lèi) widget 可讓您在水平(Row)和垂直(Column)方向上創(chuàng)建靈活的布局。其設(shè)計(jì)是基于 Web 開(kāi)發(fā)中的 Flexbox 布局模型。
- Stack?(opens new window): 取代線性布局 (和 Android 中的
FrameLayout
相似),[Stack
](https://docs.flutter.dev/flutter/ widgets/Stack-class.html)允許子 widget 堆疊, 你可以使用?Positioned?(opens new window)來(lái)定位他們相對(duì)于Stack
的上下左右四條邊的位置。Stacks是基于Web開(kāi)發(fā)中的絕對(duì)定位(absolute positioning )布局模型設(shè)計(jì)的。- Container?(opens new window):?Container?(opens new window)可讓您創(chuàng)建矩形視覺(jué)元素。Container 可以裝飾一個(gè)BoxDecoration?(opens new window), 如 background、一個(gè)邊框、或者一個(gè)陰影。?Container?(opens new window)也可以具有邊距(margins)、填充(padding)和應(yīng)用于其大小的約束(constraints)。另外,?Container?(opens new window)可以使用矩陣在三維空間中對(duì)其進(jìn)行變換。
1.9.2 Material組件
?要使用Material組件,需要先導(dǎo)入:
import 'package:flutter/material.dart';
- Flutter 提供了一套豐富 的Material 組件,它可以幫助我們構(gòu)建遵循 Material Design 設(shè)計(jì)規(guī)范的應(yīng)用程序。
- Material 應(yīng)用程序以MaterialApp?(opens new window)?組件開(kāi)始, 該組件在應(yīng)用程序的根部創(chuàng)建了一些必要的組件,比如
Theme、
Scaffold
、AppBar
、TextButton
等。- Material 組件庫(kù)中有一些組件可以根據(jù)實(shí)際運(yùn)行平臺(tái)來(lái)切換表現(xiàn)風(fēng)格,比如
MaterialPageRoute
,在路由切換時(shí),如果是 Android 系統(tǒng),它將會(huì)使用 Android 系統(tǒng)默認(rèn)的頁(yè)面切換動(dòng)畫(huà)(從底向上);如果是 iOS 系統(tǒng),它會(huì)使用 iOS 系統(tǒng)默認(rèn)的頁(yè)面切換動(dòng)畫(huà)(從右向左)。
1.9.3 ?Cupertino組件
?要使用Cupertino組件,需要先導(dǎo)入:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-586928.html
import 'package:flutter/cupertino.dart';
Flutter 也提供了一套豐富的 Cupertino 風(fēng)格的組件,盡管目前還沒(méi)有 Material 組件那么豐富,但是它仍在不斷的完善中。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-586928.html
/*
一個(gè)簡(jiǎn)單的 Cupertino 組件風(fēng)格的頁(yè)面
*/
//導(dǎo)入cupertino widget 庫(kù)
import 'package:flutter/cupertino.dart';
class CupertinoTestRoute extends StatelessWidget {
const CupertinoTestRoute({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text("Cupertino Demo"),
),
child: Center(
child: CupertinoButton(
color: CupertinoColors.activeBlue,
child: const Text("Press"),
onPressed: () {}
),
),
);
}
}
1.10 總結(jié)
- Flutter 的 widget 類(lèi)型分為
StatefulWidget
?和?StatelessWidget
?兩種,widget 是構(gòu)建Flutter應(yīng)用的基石。- Flutter 提供了豐富的組件,在實(shí)際的開(kāi)發(fā)中可以根據(jù)需要隨意使用它們,而不必?fù)?dān)心引入過(guò)多組件庫(kù)會(huì)讓?xiě)?yīng)用安裝包變大,這不是 web 開(kāi)發(fā),dart 在編譯時(shí)只會(huì)編譯你使用了的代碼。
- 由于 Material 和 Cupertino 都是在基礎(chǔ)組件庫(kù)之上的,所以如果我們的應(yīng)用中引入了這兩者之一,則不需要再引入
flutter/ widgets.dart
了,因?yàn)樗鼈儍?nèi)部已經(jīng)引入過(guò)了。
到了這里,關(guān)于Flutter學(xué)習(xí)四:Flutter開(kāi)發(fā)基礎(chǔ)(一)Widget的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!