簡介
谷歌開發(fā)的,有自己的渲染引擎,保持Android和iOS保持一致性
渲染方式自己渲染。
- MaterialApp 類似AppDelegate 可以設(shè)置 主題樣式,home 首頁,路由routes,以及開發(fā)模式,debug or release
- Scaffold [?sk?fo?ld] 腳手架 包含系統(tǒng)的一些UI組件,例如appBar、floatingActionButton、bottomNavigationBar
- hot reload和hot restart 熱重載和熱重啟 如果修改了狀態(tài)相關(guān)的代碼則需要hot restart,否則只需要hot reload即可
布局
- alignment 布局
- Row 橫向排列 :主軸,交叉軸(X軸)
- Column 縱向排列:主軸,交叉軸(Y軸)
- Stack 層級排列,最大的在最下面(Z軸)
- Expanded 自動填充:子部件隨著父部件的大小自己填充
- column 布局 設(shè)置高度無意義
- row 布局 設(shè)置寬度無意義
生命周期
- 初始化
- 構(gòu)造函數(shù) > initState > didChangeDependencies > Widget build
- 狀態(tài)變化
- 熱重載:reassemble > 組件狀態(tài)改變:didUpdateWidget > widget build
- 組件移除
- 頁面銷毀的時(shí)候會依次執(zhí)行:deactivate > dispose
- 切至后臺
- didChangeAppLifecycleState(
AppLifecycleState.inactive) -> didChangeAppLifecycleState(AppLifecycleState.paused) -> build
- didChangeAppLifecycleState(
- 切回前臺
- didChangeAppLifecycleState(
AppLifecycleState.inactive) -> didChangeAppLifecycleState(AppLifecycleState.resumed) -> build
- didChangeAppLifecycleState(
StatelessWidget - 生命周期
StatelessWidget 的生命周期只有一個,就是 build
build 是用來創(chuàng)建 Widget 的,但因?yàn)?build 在每次界面刷新的時(shí)候都會調(diào)用,所以不要在 build 里寫業(yè)務(wù)邏輯,可以把業(yè)務(wù)邏輯寫到你的 StatelessWidget 的構(gòu)造函數(shù)里。
class TestWidget extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
print('StatelessWidget build');
return Text('Test');
}
}
StatefulWidget - 生命周期
依次為
- createState
- initState
- didChangeDependencies
- build
- addPostFrameCallback
- didUpdateWidget
- deactivate
- dispose

狀態(tài)管理
- StatelessWidget(無狀態(tài))
一旦創(chuàng)建就不會發(fā)生變化,定義屬性值可以變化,但不會重新渲染UI,
- StatefulWidget(有狀態(tài))
- state發(fā)生變化時(shí)會重新渲染UI,類似于Hot Reload
- 更新/刷新操作:setState(() {});
- createState 此方法返回狀態(tài)管理類,進(jìn)行關(guān)聯(lián)
需要兩個類去管理
- 描述UI
- 記錄狀態(tài)的State (w,h)不銷毀,界面消失才會被干掉 : State屬性改變時(shí)會根據(jù)寬高重新渲染UI
頁面Push 和pop
Push
//push 跳轉(zhuǎn)新頁面
Navigator.of(context).push(
MaterialPageRoute(builder: (BuildContext context){
return DiscoverChildPage(title: this.title,);
})
);
Pop
Navigator.pop(context);
Flutter混編
Channel
MethodChannel 傳遞方法 一次通訊
- 1.通過
setInitialRoute向Flutter傳入?yún)?shù) - 2.Flutter接收傳入?yún)?shù)并顯示不同內(nèi)容
window.defaultRouteName - 3.setMethodCallHandler 判斷
[call.method isEqualToString:@"picture"]
BasicMessageChannel 傳遞字符串
監(jiān)聽輸入框輸入文本 持續(xù)傳輸
EventChannel 傳遞數(shù)據(jù)流
常見問題
1.部件溢出
A RenderFlex overflowed by 22 pixels on the bottom.
//原因:在水平或者垂直方向上的內(nèi)容超過了父部件的大小
//解決辦法
包一層SingleChildScrollView,讓你的頁面可以滑動起來。
在Scaffold中設(shè)置resizeToAvoidBottomInset為false。默認(rèn)為ture,防止部件被遮擋。如果使用了這個方法,如果底部有輸入框,則會造成遮擋。
2.輸入框遮擋
Column配合Expanded來實(shí)現(xiàn)
3.SafeArea
一旦有部件固定在頂部或者底部(嚴(yán)謹(jǐn)點(diǎn)的話可以說是在屏幕的四邊)。那我我們最好使用SafeArea來包一下。因?yàn)锳ndroid 和 IOS都有狀態(tài)欄,甚至IOS還有叫做“HomeIndicator”的橫條。所以一不留神就會出現(xiàn)適配問題
使用方法為
Material( // 需要顏色填充到邊界區(qū)域可以使用
color: Colors.white,
child: SafeArea(
child: Container(),
),
)
4.注意平臺差異
注意部分組件在Android與IOS平臺之間的差異。
Scaffold的 AppBar,AppBar中默認(rèn)的title在Android中靠左顯示,IOS中居中顯示。如果需要兩個平臺效果統(tǒng)一,需要設(shè)置在AppBar中主動設(shè)置centerTitle屬性。同時(shí)AppBar的返回箭頭圖標(biāo)也不相同,統(tǒng)一的話需要自定義leading。
頁面跳轉(zhuǎn)如果使用MaterialPageRoute來做過渡效果,注意Android中新的頁面會從屏幕底部滑動到屏幕頂部,IOS中新的頁面會從屏幕右側(cè)滑動到屏幕左側(cè)。
如果需要兩個平臺效果統(tǒng)一,我們不使用自帶效果,可以自定義一個。
Navigator.push(context, PageRouteBuilder(transitionDuration: Duration(milliseconds: 300),
pageBuilder: (context, animation, secondaryAnimation){
return new FadeTransition( //使用漸隱漸入過渡,
opacity: animation,
child: TestPage(),
);
})
);
5.保持頁面狀態(tài)
比如點(diǎn)擊導(dǎo)航欄來回切換頁面,默認(rèn)情況下會丟失原頁面狀態(tài),也就是每次切換都會重新初始化頁面。這種情況解決方法就是PageView與BottomNavigationBar結(jié)合使用,同時(shí)子頁面State中繼承AutomaticKeepAliveClientMixin并重寫wantKeepAlive為true。代碼大致如下:
class _TestState extends State<Test> with AutomaticKeepAliveClientMixin{
@override
Widget build(BuildContext context) {
super.build(context);
return Container();
}
@override
bool get wantKeepAlive => true;
}
6.依賴版本問題
首先這里建議凡是Flutter的插件在填寫版本號時(shí)不要使用^符號。

^符號意味著你可以使用此插件的最新版本(大于等于當(dāng)前版本)。這會導(dǎo)致什么問題呢?可能你前一天代碼還能跑起來,今天就編譯出錯了。因?yàn)檫@些插件中包括Android、IOS的所用依賴環(huán)境配置,常見的就是新版本使用了AndroidX的依賴,但是還有些插件并沒有使用AndroidX,導(dǎo)致了兩者的沖突。
7.常見第三方使用
7-1.Toast插件-oktoast
# Toast插件 https://github.com/OpenFlutter/flutter_oktoast
oktoast: ^2.2.0
import 'package:oktoast/oktoast.dart';
class Toast {
static show(String msg, {duration = 2000}) {
showToast(
msg,
duration: Duration(milliseconds: duration),
dismissOtherToast: true
);
}
static cancelToast() {
dismissAllToast();
}
}
7-1 圖片加載
# 圖片緩存 https://github.com/renefloor/flutter_cached_network_image
cached_network_image: ^1.1.1
/// 加載本地資源圖片
Widget loadAssetImage(String name, {double width, double height, BoxFit fit}){
return Image.asset(
Utils.getImgPath(name),
height: height,
width: width,
fit: fit,
);
}
/// 加載網(wǎng)絡(luò)圖片
Widget loadNetworkImage(String imageUrl, {String placeholder : "none", double width, double height, BoxFit fit: BoxFit.cover}){
return CachedNetworkImage(
imageUrl: imageUrl == null ? "" : imageUrl,
placeholder: (context, url) => loadAssetImage(placeholder, height: height, width: width, fit: fit),
errorWidget: (context, url, error) => loadAssetImage(placeholder, height: height, width: width, fit: fit),
width: width,
height: height,
fit: fit,
);
}
8.Redux 和 flutter_redux 詳解
- Redux 是一種單向數(shù)據(jù)流,可以輕松開發(fā),維護(hù)和測試應(yīng)用程序,flutter_redux是用來簡化redux的使用
- 在Redux中,所用的狀態(tài)都儲存在Store里,這個Store會放在App頂層
- View拿到Store儲存的狀態(tài)并把它映射成視圖
- Redux讓我們不能讓View直接操作數(shù)據(jù),而是通過發(fā)起一個action來告訴Reducer,狀態(tài)改變
- 這時(shí)Reducer接收到了這個action,他就回去遍歷action 表然后找到匹配的action, 根據(jù)action生成新的狀態(tài)放在Store中
- Store丟棄了老的狀態(tài)對象,儲存了新的狀態(tài)對象后,就通知所有使用到了這個狀態(tài)的View更新
- ==能夠同步不同View種的狀態(tài)==
View 發(fā)起一個狀態(tài)改變的action-> Reducer(狀態(tài)生成器)找到匹配的action, 根據(jù)action生成新的狀態(tài)放在Store中 ->通知所有使用到了這個狀態(tài)的View更新,進(jìn)而同步不同View的狀態(tài)
使用
- ①添加依賴
- ②創(chuàng)建State @immutable
- ③創(chuàng)建action
- ④創(chuàng)建reducer : 狀態(tài)生成器,它接收一個我們原來的狀態(tài),然后接收一個action,再匹配這個action生成一個新的狀態(tài)
- ⑤創(chuàng)建store : 將store儲存在應(yīng)用的入口,并初始化應(yīng)用狀態(tài)
- ⑥將Store放入頂層 :
StoreProvider,接收一個store,和child Widget - ⑦在子頁面中獲取Store中的state :
StoreConnector能夠通過StoreProvider找到頂層的store。而且能夠在state發(fā)生變化時(shí)rebuilt Widget

補(bǔ)充
1.Flutter vs ReactNative 渲染機(jī)制
- RN的效率由于是將View編譯成了原生View,效率比HTML5高很多,但它也有效率問題,RN的渲染機(jī)制是基于前端框架的考慮,復(fù)雜的UI渲染是需要依賴多個view疊加
- Flutter在渲染技術(shù)上,選擇了自己實(shí)現(xiàn),直接通過 skia 渲染,有更好的可控性,使用了新的語言Dart,避免了RN的那種通過橋接器與Javascript通訊導(dǎo)致效率低下的問題,性能優(yōu)于RN.
ReactNative
- 采用Javascript開發(fā),需學(xué)React,成本高
- 需要JavaScript橋接器,實(shí)現(xiàn)JS到Native轉(zhuǎn)化,性能耗損
- 訪問原生UI,頻繁操作易出性能問題
- 支持線上動態(tài)性,可有效避免頻繁更新版本
Flutter
- 采用Dart開發(fā),可直接編譯成Native代碼(易學(xué))
- 自帶UI組件和渲染器,僅依賴系統(tǒng)提供的Canvas(無橋接耗損)
- 暫不支持線上動態(tài)性,目前Android支持,ios不支持
2.setState方法是立即生效嗎?
setState 其實(shí)是調(diào)用了 markNeedsBuild ,該方法內(nèi)部標(biāo)記此Element 為 Dirty ,然后在下一幀 WidgetsBinding.drawFrame 才會被繪制,這可以看出 setState 并不是立即生效的。