在Flutter中,Widget是一切的基礎(chǔ)火惊,作為響應(yīng)式渲染,屬于MVVM的實(shí)現(xiàn)機(jī)制奔垦,通過修改數(shù)據(jù),再用setState設(shè)置數(shù)據(jù)尸疆,F(xiàn)lutter會(huì)自動(dòng)通過綁定的數(shù)據(jù)更新Widget椿猎。
1.StatelessWidget 和 StatefulWidget
在平時(shí),用的最多就是StatelessWidget和StatefulWidget這兩種Widget寿弱,StatelessWidget表示無狀態(tài)的犯眠,StatefulWidget表示有狀態(tài)的。
在Flutter中每個(gè)頁面都是一幀症革,無狀態(tài)就是保持在那一幀筐咧,總而言之就是不能跟用戶交互,當(dāng)有狀態(tài)的Widget當(dāng)數(shù)據(jù)更新時(shí),其實(shí)是繪制了新的Widget量蕊,也就是UI發(fā)生了變化铺罢,只是State實(shí)現(xiàn)了跨幀數(shù)據(jù)同步保存。
1)StatelessWidget
源碼:
abstract class StatelessWidget extends Widget {
// 初始化子類的[key]残炮。這個(gè)key類是Widget韭赘、Element、SemanticsNode的唯一標(biāo)識符
// 是用來控制Widget數(shù)中替換Widget的時(shí)候使用的势就。
const StatelessWidget({ Key key }) : super(key: key);
// 創(chuàng)建一個(gè)[StatelessElement]來管理這個(gè)小部件在樹中的位置
@override
StatelessElement createElement() => StatelessElement(this);
// 描述這部件呈現(xiàn)用戶界面的部分
@protected
Widget build(BuildContext context);
}
函數(shù):Widget build(BuildContext context):
作用:用于描述當(dāng)前widget所代表的UI
調(diào)用時(shí)機(jī):
- Widget首次插入樹中時(shí)調(diào)用
- 父節(jié)點(diǎn)更改了配置
- 它所依賴的InheritedWidget改變時(shí)
import 'package:flutter/material.dart';
//使用`flutter/material.dart` 目的是使用Matrial風(fēng)格的小控件
void main(){
// 類比iOS入口main函數(shù)
//運(yùn)行程序
runApp(MyApp('你是豬嗎'));
}
//繼承無狀態(tài)的StatelessWidget 使程序自身變?yōu)閃iget
class MyApp extends StatelessWidget{
//要顯示的內(nèi)容
final String text;
//數(shù)據(jù)內(nèi)容可以通過構(gòu)造方法傳遞進(jìn)來
MyApp(this.text);
//重寫build方法 返回你需要的控件
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
//紅色背景
color: Colors.red,
//高度 現(xiàn)在沒用 會(huì)撐滿整個(gè)屏幕
height: 200,
//寬度 運(yùn)行效果會(huì)撐滿整個(gè)屏幕
width: 200,
//內(nèi)容居中
alignment: Alignment.center,
//Text控件
child: new Text(
text ?? "hello",
textDirection: TextDirection.ltr,//文本展示方向泉瞻,從左向右。需要加上這句不然報(bào)錯(cuò)
),
);
}
}
2)StatefulWidget
有狀態(tài)的控件,狀態(tài)是在創(chuàng)建控件可以同步讀取信息苞冯,并且在控件的生命周期內(nèi)可以改變袖牙,當(dāng)控件狀態(tài)發(fā)生改變時(shí)使用State.setState來及時(shí)更新。
源碼:
abstract class StatefulWidget extends Widget {
const StatefulWidget({ Key key }) : super(key: key);
@override
StatefulElement createElement() => StatefulElement(this);
@protected
State createState();
}
函數(shù):State createState()
作用:在Widget樹中給定的位置上舅锄,創(chuàng)建此可變狀態(tài)的小部件贼陶,子類應(yīng)該重寫此方法返回新建的關(guān)聯(lián)子類的實(shí)例。
調(diào)用時(shí)機(jī):當(dāng)調(diào)用一個(gè)StatefulWidget巧娱,框架就會(huì)調(diào)用createState這個(gè)方法碉怔,當(dāng)一個(gè)StatefulWidget從Widget樹中移除,再次插入樹中禁添,那么會(huì)再次調(diào)用createState來創(chuàng)建一個(gè)新的State對象撮胧,這樣做簡化了State對象的生命周期。
StatefulWidget本身是不可變的老翘,它可變的狀態(tài)存儲在與之關(guān)聯(lián)的State對象中芹啥。
State對象生命周期;
- 初始化時(shí)期 initState
- 更新時(shí)期 didChangeDependencies 铺峭、build 墓怀、didUpdateWidget
- 銷毀時(shí)期 deactivate 和 dispose
initState:類似于iOS的viewDidLoad(),在這個(gè)方法中通常會(huì)做一些初始化工作。
didChangeDependencies:這個(gè)方法在兩種情況下會(huì)調(diào)用卫键,當(dāng)調(diào)用initState方法時(shí)調(diào)用傀履;當(dāng)從其他對象依賴的數(shù)據(jù)發(fā)生變化時(shí)調(diào)用,如InheritedWidget莉炉。
didUpdateWidget:這是一個(gè)不常用到的生命周期方法钓账,當(dāng)父組件需要重新繪制時(shí)才會(huì)調(diào)用。
build: 這是一個(gè)必須實(shí)現(xiàn)的方法絮宁,在這里實(shí)現(xiàn)你要顯示的頁面內(nèi)容梆暮,它會(huì)在didChangeDependencies方法之后立即調(diào)用,另外當(dāng)調(diào)用setState()后也會(huì)再次調(diào)用該build方法绍昂。
deactivate:很少使用啦粹,在組件被移除時(shí)候調(diào)用偿荷,在dispose之前調(diào)用。
dispose:常用唠椭,組件被銷毀時(shí)候調(diào)用跳纳。通常在該方法中執(zhí)行一些資源的釋放工作。
import 'package:flutter/material.dart';
//使用`flutter/material.dart` 目的是使用Matrial風(fēng)格的小控件
import 'dart:async';//記得導(dǎo)庫
void main(){
//運(yùn)行程序
runApp(StateWidget());
}
//繼承StatefulWidget
class StateWidget extends StatefulWidget{
@override
State createState(){
return _StateWidget();
}
}
//控件繼承State
class _StateWidget extends State<StateWidget>{
int number = 0;
String text;
//構(gòu)造函數(shù)
_StateWidget(this.text);
@override
void initState(){
//初始化泪蔫,這個(gè)函數(shù)在控件的生命周期內(nèi)調(diào)用一次
super.initState();
print("進(jìn)入initState");
//3秒后改變text的內(nèi)容
new Future.delayed(const Duration(seconds: 3),(){
setState(() {
number++;
text = "已經(jīng)改變數(shù)值棒旗,數(shù)值現(xiàn)在是$number";
});
});
}
@override
void dispose(){
//銷毀
super.dispose();
print('銷毀');
}
@override
void didChangeDependencies(){
//在initState之后調(diào)
super.didChangeDependencies();
print('進(jìn)入didChange');
}
//重寫build方法
@override
Widget build(BuildContext context){
return Container(
//紅色背景
color: Colors.red,
//內(nèi)容居中
alignment: Alignment.center,
//Text控件
child: new Text(
//Dart語法中 ?? 表示如果text為空,就會(huì)返回??號的內(nèi)容
text ?? "沒改變數(shù)值",
textDirection: TextDirection.ltr,//需要加上這句不然報(bào) RichText widgets require a Directionality widget ancestor.
),
);
}
}
3)StatelessWidget和StatefulWidget使用選擇撩荣,以及性能問題铣揉。
- 樹根上盡量不用狀態(tài)控件,因?yàn)槿绻麛?shù)據(jù)有變化樹根每次都更新餐曹,那就是整棵樹都要重建逛拱,把狀態(tài)用在樹葉上,這樣更新的時(shí)候只會(huì)更新自己台猴。
- 減少build方法所創(chuàng)建的節(jié)點(diǎn)數(shù)量和控件數(shù)量朽合。
- 利用緩存,如果子樹中不更改饱狂,將子樹中緩存起來曹步,每次使用其子樹時(shí)重新使用它适室,學(xué)會(huì)重用思想月弛。
- 盡可能使用const修飾控件吻谋。
2.主體結(jié)構(gòu)組件
1)MaterialApp和CupertinoApp
Flutter 中包含兩套風(fēng)格的組件逗栽,分別是 Material 和 Cupertino 。
MaterialApp 是由 Google 推出的 Material Design設(shè)計(jì)風(fēng)格的組件埠况。
Cupertino 是 iOS風(fēng)格的組件夭坪,命名都帶 Cupertino 前綴将宪,比如 CupertinoSlider 雏婶、CupertinoDatePicker等物赶。
二者屬性基本相同,用法相似留晚,CupertinoApp有些會(huì)帶Cupertino前綴酵紫,常用屬性介紹;
- title : 這個(gè)和啟動(dòng)圖標(biāo)名字是不一樣的倔丈,在任務(wù)管理窗口中所顯示的應(yīng)用名字憨闰。
- theme : 應(yīng)用各種 UI 所使用的主題顏色。
- color : 應(yīng)用的主要顏色值(primary color)需五,也就是安卓任務(wù)管理窗口中所顯示的應(yīng)用顏色。
- home : 應(yīng)用默認(rèn)所顯示的界面Widget轧坎。
- routes : 定義應(yīng)用中頁面跳轉(zhuǎn)規(guī)則宏邮,當(dāng)使用 Navigator.pushNamed 來路由的時(shí)候,會(huì)在 routes 查找路由名字,然后使用 對應(yīng)的 WidgetBuilder 來構(gòu)造一個(gè)帶有頁面切換動(dòng)畫的 MaterialPageRoute蜜氨。
- initialRoute :第一個(gè)顯示的路由名字械筛,默認(rèn)值為 Window.defaultRouteName。
- onGenerateRoute:生成路由的回調(diào)函數(shù)飒炎,當(dāng)導(dǎo)航的命名路由的時(shí)候埋哟,會(huì)使用這個(gè)來生成界面。
- onLocaleChanged:當(dāng)系統(tǒng)修改語言的時(shí)候郎汪,會(huì)觸發(fā)這個(gè)回調(diào)赤赊。
- navigatorObservers :應(yīng)用 Navigator 的監(jiān)聽器。
- debugShowMaterialGrid:是否顯示紙墨設(shè)計(jì)基礎(chǔ)布局網(wǎng)格煞赢,用來調(diào)試 UI 的工具抛计。
- showPerformanceOverlay :顯示性能標(biāo)簽。
- checkerboardRasterCacheImages 照筑、showSemanticsDebugger吹截、debugShowCheckedModeBanner 各種調(diào)試開關(guān)。
2)Scaffold
Scaffold 翻譯過來就是腳手架的意思凝危,一個(gè)提供 Material Design 設(shè)計(jì)中基本布局的 widget波俄。
常用屬性介紹:
- appBar:導(dǎo)航欄。
- body: 用于顯示當(dāng)前界面主要內(nèi)容的Widget蛾默。
- floatingActionButton:一個(gè)懸浮在body上的按鈕懦铺,默認(rèn)顯示在右下角。
- floatingActionButtonLocation:用于設(shè)置floatingActionButton顯示的位置趴生。
- floatingActionButtonAnimator:floatingActionButton移動(dòng)到一個(gè)新的位置時(shí)的動(dòng)畫阀趴。
- persistentFooterButtons:多狀態(tài)按鈕。
- drawer:左側(cè)的抽屜菜單苍匆。
- endDrawer:右'側(cè)的抽屜菜單刘急。
- bottomNavigationBar:底部導(dǎo)航欄,同iOS的tabbar。
- bottomSheet:顯示在底部的工具欄浸踩。
- backgroundColor:內(nèi)容背景顏色叔汁。
- resizeToAvoidBottomPadding :控制界面內(nèi)容 body 是否重新布局來避免底部被覆蓋,比如當(dāng)鍵盤顯示的時(shí)候检碗,重新布局避免被鍵盤蓋住內(nèi)容据块。
3)AppBar
應(yīng)用導(dǎo)航欄,相當(dāng)于iOS中的navigationBar折剃。
常用屬性介紹:
- leading:設(shè)置導(dǎo)航欄返回的組件另假,相當(dāng)于iOS中的leftBarButtonItem。
- title:導(dǎo)航欄標(biāo)題
- actions:設(shè)置導(dǎo)航欄右邊的組件怕犁,一個(gè)Widget列表边篮,相當(dāng)于iOS中的rightBarButtonItems己莺。
- bottom:一個(gè) AppBarBottomWidget 對象,通常是 TabBar戈轿。用來在 Toolbar 標(biāo)題下面顯示一個(gè) Tab 導(dǎo)航欄凌受。
- backgroundColor: Appbar 的顏色,默認(rèn)值為 ThemeData.primaryColor思杯。
- brightness:Appbar的亮度胜蛉,有白色和黑色兩種主題,默認(rèn)值為 ThemeData.primaryColorBrightness色乾。
- iconTheme:Appbar上圖標(biāo)的顏色誊册、透明度、和尺寸信息杈湾。默認(rèn)值為 ThemeData.primaryIconTheme解虱。
- textTheme:Appbar 上的文字樣式。
- centerTitle:標(biāo)題是否居中顯示漆撞,默認(rèn)值根據(jù)不同的操作系統(tǒng)殴泰,顯示方式不一樣。
4)TabBar
標(biāo)簽導(dǎo)航組件浮驳,類似于新聞或者電商樣式中有關(guān)導(dǎo)航條下面有一個(gè)可以滾動(dòng)的一行按鈕悍汛,一般和AppBar連用,設(shè)置AppBar的bottom屬性至会。
常用屬性:
- tabs:必須實(shí)現(xiàn)的离咐,設(shè)置需要展示的tabs,最少需要兩個(gè)奉件。
- controller:標(biāo)簽選擇變化控制器宵蛀,TabController對象,默認(rèn)的DefaultTabController或者自定義TabController。
- isScrollable:是否可滾動(dòng)县貌。
- indicator:用于設(shè)定選中狀態(tài)下的展示樣式术陶。
- indicatorColor:選中指示器的顏色。
- indicatorWeight:選中下劃線的高度煤痕,值越大高度越高梧宫,默認(rèn)為2。
- indicatorPadding:底部指示器的Padding摆碉。
- indicatorSize:指示器大小計(jì)算方式塘匣,TabBarIndicatorSize.label跟文字等寬,TabBarIndicatorSize.tab跟每個(gè)tab等寬。
- labelColor:選中l(wèi)abel顏色巷帝。
- labelStyle:選中l(wèi)abel的Style忌卤。
- labelPadding:每個(gè)label的padding值。
- unselectedLabelColor:未選中l(wèi)abel顏色楞泼。
- unselectedLabelStyle:未選中l(wèi)abel的Style埠巨。
- onTap:點(diǎn)擊事件
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'demo',
theme: ThemeData(
//設(shè)置為藍(lán)色
primarySwatch: Colors.blue
),
home: MainWidget(),
);
}
}
class MainWidget extends StatelessWidget{
final List <Widget> tabs = [
Text('語文'),
Text('數(shù)學(xué)' ),
Text('英語'),
Text('物理'),
Text('化學(xué)'),
Text('生物'),
];
@override
Widget build(BuildContext context) {
// TODO: implement build
return DefaultTabController(
length: tabs.length,
child: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text('demo'),
bottom: TabBar(tabs: tabs,
onTap: (int index) {
print('Selected......$index');
},
unselectedLabelColor: Colors.white,
labelColor: Colors.black,
labelStyle: TextStyle(fontSize: 20.0),
isScrollable: true,
indicatorColor: Colors.red,
indicatorSize: TabBarIndicatorSize.label,
indicatorWeight: 2.0,
),
),
//選中哪個(gè)tab历谍,body就會(huì)顯示TabBarView中child屬性的widget
body: TabBarView(children: tabs),
),
);
}
}
5)BottomNavigationBar
底部導(dǎo)航欄控件现拒,提供了頂級視圖之間的快速導(dǎo)航辣垒。
常用屬性:
- currentIndex:當(dāng)前選item索引項(xiàng)。在onTap中控制當(dāng)前選中的item實(shí)現(xiàn)切換tab頁印蔬。
- type:有兩個(gè)值勋桶,默認(rèn)值BottomNavigationBarType.shifting和BottomNavigationBarType.fixed,一般使用fixed。
- backgroundColor:背景顏色侥猬。
- selectedItemColor:選中item顏色例驹。
- unselectedItemColor:未選中item顏色。
- selectedFontSize:選中item文字大小退唠。
- unselectedFontSize:未選中item文字大小鹃锈。
- showUnselectedLabels:是否顯示未選中的Item的文字。
class _MainWidgetState extends State<MainWidget>{
final List <Widget> tabs = [
Text('語文'),
Text('數(shù)學(xué)'),
Text('英語'),
Text('物理'),
];
//底部Tab數(shù)據(jù)
final Map bottomMap ={
"首頁":Icon(Icons.home),
"朋友圈":Icon(Icons.camera),
"信息":Icon(Icons.message),
"其他":Icon(Icons.devices_other),
};
List <BottomNavigationBarItem> tabItems(){
var itemList = <BottomNavigationBarItem>[];
bottomMap.forEach((key, value) {
var item = BottomNavigationBarItem(
title: Text(key),
icon: value,
// backgroundColor: Colors.blue
);
itemList.add(item);
});
return itemList;
}
var _index = 0;
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text('demo'),
),
body: tabs[_index],
bottomNavigationBar: BottomNavigationBar(
items: tabItems(),
backgroundColor: Colors.blue,
selectedItemColor: Colors.white,
unselectedItemColor: Colors.orange,
type: BottomNavigationBarType.fixed,
selectedFontSize: 12,
currentIndex: _index,
onTap: (idx){
print("tab index $idx");
setState(() {
_index = idx;
});
},
),
);
}
}