Flutter開(kāi)發(fā)實(shí)戰(zhàn)初級(jí)(一)ListView詳解
- 本篇博客主要以一個(gè)demo的形式講解ListView的使用
源碼下載點(diǎn)擊這里:flutter_listview_demo
先來(lái)看一下效果圖亚侠,下面是運(yùn)行在iphone11上面的:
ListView 知識(shí)點(diǎn)
在Flutter中,用ListView來(lái)顯示列表項(xiàng),支持垂直和水平方向展示,通過(guò)一個(gè)屬性我們就可以控制其方向
1.水平的列表
2.垂直的列表
3.數(shù)據(jù)量非常大的列表
4.內(nèi)置的ListTile(挺好用的)
ListView Demo
1.demo 下載地址:flutter_listviewdemo
2.運(yùn)行效果:
一. 新建car.dart 保存模型信息
1.定義一個(gè)Car
class Car {
const Car({
this.name,
this.imageUrl,
});
final String name;
final String imageUrl;
}
2.定義一個(gè)數(shù)組保存Car對(duì)象
//模型數(shù)組
final List<Car> datas = [
Car(
name: '保時(shí)捷918 Spyder',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-7d8be6ebc4c7c95b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: '蘭博基尼Aventador',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-e3bfd824f30afaac?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: '法拉利Enzo',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-a1d64cf5da2d9d99?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: 'Zenvo ST1',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-bf883b46690f93ce?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: '邁凱倫F1',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-5a7b5550a19b8342?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: '薩林S7',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-2e128d18144ad5b8?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: '科尼賽克CCR',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-01ced8f6f95219ec?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: '布加迪Chiron',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-7fc8359eb61adac0?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: '軒尼詩(shī)Venom GT',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-d332bf510d61bbc2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: '西貝爾Tuatara',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-3dd9a70b25ae6bc9?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
)
];
二. 新建carlistview.dart 用來(lái)展示列表數(shù)據(jù)
1.定義Listview 展示數(shù)據(jù)
@override
Widget build(BuildContext context) {
// TODO: implement build
return ListView.builder(
//控制方向 默認(rèn)是垂直的
// scrollDirection: Axis.horizontal, //控制水平方向顯示
/* children: <Widget>[
_getContainer('Maps', Icons.map),
_getContainer('phone', Icons.phone),
_getContainer('Maps', Icons.map),
], */
itemCount: datas.length, //告訴ListView總共有多少個(gè)cell
itemBuilder: _cellForRow //使用_cellForRow回調(diào)返回每個(gè)cell
);
}
2.定義一個(gè)回調(diào)函數(shù)哼蛆,返回每個(gè)cell
Widget _cellForRow(BuildContext context, int index) {
return Container(
color: Colors.white,
margin: EdgeInsets.all(10),
child: Column(
children: <Widget>[
Image.network(
datas[index].imageUrl
),
SizedBox(
height: 10,
),
Text(
datas[index].name,
style: TextStyle(
fontWeight: FontWeight.w800,
fontSize: 18.0,
fontStyle: FontStyle.values[1]
),
),
Container(height: 20,),
],
), //每人一輛跑車(chē)
);
}
三. main.dart 調(diào)用ListView
import 'package:flutter/material.dart';
import 'model/carlistview.dart';
//如果只有一行代碼蛤育,可以是 => 代替 {}
void main() => runApp(KYLApp());
class KYLApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Home(),
theme: ThemeData(
primaryColor: Colors.yellow
),
);
}
}
class Home extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
backgroundColor: Colors.grey[100],
appBar: AppBar(
title: Text('kongyulu first app'),
),
body: ListViewDemo(),
);
}
}
四. 知識(shí)點(diǎn)講解
main函數(shù)使用了(=>)符號(hào), 這是Dart中單行函數(shù)或方法的簡(jiǎn)寫(xiě)坟冲。
該應(yīng)用程序繼承了 StatelessWidget,這將會(huì)使應(yīng)用本身也成為一個(gè)widget。 在Flutter中,大多數(shù)東西都是widget逝她,包括對(duì)齊(alignment)、填充(padding)和布局(layout)
Scaffold 是 Material library 中提供的一個(gè)widget, 它提供了默認(rèn)的導(dǎo)航欄睬捶、標(biāo)題和包含主屏幕widget樹(shù)的body屬性汽绢。widget樹(shù)可以很復(fù)雜。
widget的主要工作是提供一個(gè)build()方法來(lái)描述如何根據(jù)其他較低級(jí)別的widget來(lái)顯示自己侧戴。
使用外部包
1.1 Widget
1.1.1 Widget基本概念
Stateless widgets 是不可變的, 這意味著它們的屬性不能改變 - 所有的值都是最終的.
Stateful widgets 持有的狀態(tài)可能在widget生命周期中發(fā)生變化. 實(shí)現(xiàn)一個(gè) stateful widget 至少需要兩個(gè)類(lèi):
一個(gè) StatefulWidget類(lèi)宁昭。
一個(gè) State類(lèi)。 StatefulWidget類(lèi)本身是不變的酗宋,但是 State類(lèi)在widget生命周期中始終存在.
- Stateful(有狀態(tài)) 和 stateless(無(wú)狀態(tài)) widgets
有些widgets是有狀態(tài)的, 有些是無(wú)狀態(tài)的
如果用戶(hù)與widget交互积仗,widget會(huì)發(fā)生變化,那么它就是有狀態(tài)的.
widget的狀態(tài)(state)是一些可以更改的值, 如一個(gè)slider滑動(dòng)條的當(dāng)前值或checkbox是否被選中.
widget的狀態(tài)保存在一個(gè)State對(duì)象中, 它和widget的布局顯示分離蜕猫。
當(dāng)widget狀態(tài)改變時(shí), State 對(duì)象調(diào)用setState(), 告訴框架去重繪widget.
stateless widget 沒(méi)有內(nèi)部狀態(tài). Icon寂曹、 IconButton, 和Text 都是無(wú)狀態(tài)widget, 他們都是 StatelessWidget的子類(lèi)。
stateful widget 是動(dòng)態(tài)的. 用戶(hù)可以和其交互 (例如輸入一個(gè)表單、 或者移動(dòng)一個(gè)slider滑塊),或者可以隨時(shí)間改變 (也許是數(shù)據(jù)改變導(dǎo)致的UI更新). Checkbox, Radio, Slider, InkWell, Form, and TextField 都是 stateful widgets, 他們都是 StatefulWidget的子類(lèi)隆圆。
1.1.2 Widget之間的交互
1.1.3 Widget點(diǎn)擊事件漱挚,手勢(shì)
我們處理手勢(shì)可以使用GestureDetector組件,它是可以添加手勢(shì)的一個(gè)widget渺氧,觀察它的源碼:
class GestureDetector extends StatelessWidget {
GestureDetector({
Key key,
this.child,
this.onTapDown,
this.onTapUp,
this.onTap,
this.onTapCancel,
this.onDoubleTap,
this.onLongPress,
this.onLongPressUp,
this.onVerticalDragDown,
this.onVerticalDragStart,
this.onVerticalDragUpdate,
this.onVerticalDragEnd,
this.onVerticalDragCancel,
this.onHorizontalDragDown,
this.onHorizontalDragStart,
this.onHorizontalDragUpdate,
this.onHorizontalDragEnd,
this.onHorizontalDragCancel,
this.onPanDown,
this.onPanStart,
this.onPanUpdate,
this.onPanEnd,
this.onPanCancel,
this.onScaleStart,
this.onScaleUpdate,
this.onScaleEnd,
this.behavior,
this.excludeFromSemantics = false
})
可以看到GestureDetector的本質(zhì)就是一個(gè)普通的widget旨涝,它擁有很多的手勢(shì)onTapDown(點(diǎn)下),onTapUp(抬起),onTap(點(diǎn)擊)…等,同時(shí)也擁有child屬性,我們可以利用child繪制界面侣背,利用手勢(shì)處理點(diǎn)擊事件白华。
1.1.4 Widget 深入探索
1.首先我們需要明白,Widget 是什么贩耐?這里有一個(gè) “總所周知” 的答就是:Widget并不真正的渲染對(duì)象 弧腥。是的,事實(shí)上在 Flutter 中渲染是經(jīng)歷了從 Widget 到 Element 再到 RenderObject 的過(guò)程潮太。
2.我們都知道 Widget 是不可變的管搪,那么 Widget 是如何在不可變中去構(gòu)建畫(huà)面的?上面我們知道铡买,Widget 是需要轉(zhuǎn)化為 Element 去渲染的抛蚤,而從下圖注釋可以看到,事實(shí)上 Widget 只是 Element 的一個(gè)配置描述 寻狂,告訴 Element 這個(gè)實(shí)例如何去渲染。
那么 Widget 和 Element 之間是怎樣的對(duì)應(yīng)關(guān)系呢朋沮?從上圖注釋也可知: Widget 和 Element 之間是一對(duì)多的關(guān)系 蛇券。實(shí)際上渲染樹(shù)是由 Element 實(shí)例的節(jié)點(diǎn)構(gòu)成的樹(shù),而作為配置文件的 Widget 可能被復(fù)用到樹(shù)的多個(gè)部分樊拓,對(duì)應(yīng)產(chǎn)生多個(gè) Element 對(duì)象纠亚。
3.那么RenderObject 又是什么?它和上述兩個(gè)的關(guān)系是什么筋夏?從源碼注釋寫(xiě)著 An object in the render tree 可以看出到 RenderObject 才是實(shí)際的渲染對(duì)象蒂胞,而通過(guò) Element 源碼我們可以看出:Element 持有 RenderObject 和 Widget。
再結(jié)合下圖条篷,可以大致總結(jié)出三者的關(guān)系是:配置文件 Widget 生成了 Element骗随,而后創(chuàng)建 RenderObject 關(guān)聯(lián)到 Element 的內(nèi)部 renderObject 對(duì)象上,最后Flutter 通過(guò) RenderObject 數(shù)據(jù)來(lái)布局和繪制赴叹。 理論上你也可以認(rèn)為 RenderObject 是最終給 Flutter 的渲染數(shù)據(jù)鸿染,它保存了大小和位置等信息,F(xiàn)lutter 通過(guò)它去繪制出畫(huà)面乞巧。
4.說(shuō)到 RenderObject 涨椒,就不得不說(shuō) RenderBox :A render object in a 2D Cartesian coordinate system,從源碼注釋可以看出,它是在繼承 RenderObject 基礎(chǔ)的布局和繪制功能上蚕冬,實(shí)現(xiàn)了“笛卡爾坐標(biāo)系”:以 Top免猾、Left 為基點(diǎn),通過(guò)寬高兩個(gè)軸實(shí)現(xiàn)布局和嵌套的囤热。
RenderBox 避免了直接使用 RenderObject 的麻煩場(chǎng)景猎提,其中 RenderBox 的布局和計(jì)算大小是在 performLayout() 和 performResize() 這兩個(gè)方法中去處理,很多時(shí)候我們更多的是選擇繼承 RenderBox 去實(shí)現(xiàn)自定義赢乓。
5.綜合上述情況忧侧,我們知道:
- Widget只是顯示的數(shù)據(jù)配置,所以相對(duì)而言是輕量級(jí)的存在牌芋,而 Flutter 中對(duì) Widget 的也做了一定的優(yōu)化蚓炬,所以每次改變狀態(tài)導(dǎo)致的 Widget 重構(gòu)并不會(huì)有太大的問(wèn)題。
- RenderObject 就不同了躺屁,RenderObject 涉及到布局肯夏、計(jì)算、繪制等流程犀暑,要是每次都全部重新創(chuàng)建開(kāi)銷(xiāo)就比較大了驯击。
6.所以針對(duì)是否每次都需要?jiǎng)?chuàng)建出新的 Element 和 RenderObject 對(duì)象,Widget 都做了對(duì)應(yīng)的判斷以便于復(fù)用耐亏,比如:在 newWidget 與oldWidget 的 runtimeType 和 key 相等時(shí)會(huì)選擇使用 newWidget 去更新已經(jīng)存在的 Element 對(duì)象徊都,不然就選擇重新創(chuàng)建新的 Element。
由此可知:Widget 重新創(chuàng)建广辰,Element 樹(shù)和 RenderObject 樹(shù)并不會(huì)完全重新創(chuàng)建暇矫。
7.看到這,說(shuō)個(gè)題外話:那一般我們可以怎么獲取布局的大小和位置呢择吊?
首先這里需要用到我們前文中提過(guò)的 GlobalKey 李根,通過(guò) key 去獲取到控件對(duì)象的 BuildContext,而我們也知道 BuildContext 的實(shí)現(xiàn)其實(shí)是 Element几睛,而Element持有 RenderObject 房轿。So,我們知道的 RenderObject 所森,實(shí)際上獲取到的就是 RenderBox 囱持,那么通過(guò) RenderBox 我們就只大小和位置了。
showSizes() {
RenderBox renderBoxRed = fileListKey.currentContext.findRenderObject();
print(renderBoxRed.size);
}
showPositions() {
RenderBox renderBoxRed = fileListKey.currentContext.findRenderObject();
print(renderBoxRed.localToGlobal(Offset.zero));
}
1.2 StatelessWidget和StatefulWidget
通俗點(diǎn)講就是:
stateful組件就是和用戶(hù)交互后會(huì)有狀態(tài)變化焕济,例如滾動(dòng)條Slider洪唐。
stateless組件就是交互后沒(méi)有狀態(tài)變化,例如顯示的一個(gè)文本Text吼蚁。
1.2.1 基本概念和用法
- StatefulWidget
具有可變狀態(tài)( state)的Widget(窗口小部件).
例如系統(tǒng)提供的 Checkbox, Radio, Slider, InkWell, Form, and TextField 都是 stateful widgets, 他們都是 StatefulWidget的子類(lèi)凭需。
狀態(tài)( state) 是可以在構(gòu)建Widget時(shí)同步讀取時(shí) 和 在Widget的生命周期期間可能改變的信息
Widget實(shí)現(xiàn)者的責(zé)任就是 在狀態(tài)改變時(shí)通過(guò) State.setState. 立即通知狀態(tài)
當(dāng)您描述的用戶(hù)界面部分不依賴(lài)于對(duì)象本身中的配置信息和其中構(gòu)件被夸大的BuildContext時(shí)问欠,無(wú)狀態(tài)小部件很有用。對(duì)于可以動(dòng)態(tài)改變的組合粒蜈,例如由于具有內(nèi)部時(shí)鐘驅(qū)動(dòng)狀態(tài)顺献,或取決于某些系統(tǒng)狀態(tài),請(qǐng)考慮使用StatefulWidget枯怖。
StatefulWidget實(shí)例本身是不可變的注整,并將其可變狀態(tài)存儲(chǔ)在由createState方法創(chuàng)建的獨(dú)立狀態(tài)對(duì)象中 ,或者存儲(chǔ)在該狀態(tài)訂閱的對(duì)象中度硝,例如Stream或ChangeNotifier對(duì)象肿轨,其引用存儲(chǔ)在StatefulWidget的最終字段中本身。
該框架只要調(diào)用一個(gè)StatefulWidget就 調(diào)用createState蕊程,這意味著如果該小部件已經(jīng)插入到多個(gè)位置的樹(shù)中椒袍,那么多個(gè)State對(duì)象可能與同一個(gè)StatefulWidget關(guān)聯(lián)。同樣藻茂,如果StatefulWidget從樹(shù)中移除驹暑,后來(lái)在樹(shù)再次插入時(shí),框架將調(diào)用createState再創(chuàng)建一個(gè)新的國(guó)家目標(biāo)辨赐,簡(jiǎn)化的生命周期狀態(tài)的對(duì)象优俘。
- StatelessWidget
不需要可變狀態(tài)的小部件。
無(wú)狀態(tài)小部件是一個(gè)小部件掀序,它通過(guò)構(gòu)建一系列其他小部件來(lái)更加具體地描述用戶(hù)界面帆焕,從而描述用戶(hù)界面的一部分。構(gòu)建過(guò)程以遞歸方式繼續(xù)進(jìn)行不恭,直到用戶(hù)界面的描述完全具體(例如叶雹,完全由RenderObjectWidget組成,它描述具體的RenderObject)县袱。
當(dāng)您描述的用戶(hù)界面部分不依賴(lài)于對(duì)象本身中的配置信息和其中構(gòu)件被夸大的BuildContext時(shí),無(wú)狀態(tài)小部件很有用佑力。對(duì)于可以動(dòng)態(tài)改變的組合式散,例如由于具有內(nèi)部時(shí)鐘驅(qū)動(dòng)狀態(tài),或取決于某些系統(tǒng)狀態(tài)打颤,請(qǐng)考慮使用StatefulWidget暴拄。
無(wú)狀態(tài)小部件的構(gòu)建方法通常只在以下三種情況下調(diào)用:第一次將小部件插入樹(shù)中,第一次在小部件的父級(jí)更改其配置時(shí)以及第二次使用InheritedWidget時(shí)编饺,它依賴(lài)于更改乖篷。
如果一個(gè)小部件的父節(jié)點(diǎn)會(huì)定期更改小部件的配置,或者如果它依賴(lài)于頻繁更改的繼承小部件透且,那么優(yōu)化構(gòu)建方法的性能以保持流暢的渲染性能非常重要撕蔼。
有幾種技術(shù)可以用來(lái)最小化重建無(wú)狀態(tài)小部件的影響:
最小化構(gòu)建方法及其創(chuàng)建的任何小部件傳遞創(chuàng)建的節(jié)點(diǎn)數(shù)量豁鲤。例如,可以考慮只使用一個(gè)Align或一個(gè) CustomSingleChildLayout鲸沮,而不是精心安排Row s琳骡,Column s,Padding s和SizedBox es來(lái)定位一個(gè)單獨(dú)的孩子讼溺。您可以考慮使用單個(gè)CustomPaint小部件楣号,而不是使用多個(gè)Container的復(fù)雜分層和裝飾 s來(lái)繪制恰當(dāng)?shù)膱D形效果。
const盡可能使用小部件怒坯,并為小部件提供const構(gòu)造函數(shù)炫狱,以便小部件的用戶(hù)也可以這樣做。
考慮將無(wú)狀態(tài)小部件重構(gòu)為有狀態(tài)的小部件剔猿,以便它可以使用StatefulWidget中描述的一些技術(shù)视译,例如緩存子樹(shù)的公共部分,并在更改樹(shù)結(jié)構(gòu)時(shí)使用GlobalKey艳馒。
如果由于使用了InheritedWidget憎亚,小部件可能會(huì)經(jīng)常重建 ,請(qǐng)考慮將無(wú)狀態(tài)小部件重構(gòu)為多個(gè)小部件弄慰,并將更改后的樹(shù)部分推送到樹(shù)葉第美。例如,不是構(gòu)建一個(gè)具有四個(gè)小部件的樹(shù)陆爽,最內(nèi)部的小部件取決于主題什往,而是考慮將構(gòu)建最內(nèi)部小部件的構(gòu)建函數(shù)的部分分解到其自己的小部件中,以便只有最內(nèi)部的小部件當(dāng)主題改變時(shí)需要重建慌闭。
1.2.2 源碼分析
Flutter的Widget有StatelessWidget和StatefulWidget兩個(gè)子類(lèi)(當(dāng)然還有其他子類(lèi)别威,此處暫且不談),二者的的使用方式大致模板代碼如下:
//StatelessWidget的使用模板代碼
class StatelessWidgetDemo extends StatelessWidget{
@override
Widget build(BuildContext context) {
return null;///返回創(chuàng)建的頁(yè)面
}
}
//StatefulWidget的使用方式模板代碼
class StatefulWidgetDemo extends StatefulWidget{
@override
State<StatefulWidget> createState() {
//創(chuàng)建state對(duì)象
return _State();
}
}
class _State extends State<StatefulWidgetDemo>{
//創(chuàng)建頁(yè)面
@override
Widget build(BuildContext context) {
return null;
}
}
這是典型的模板設(shè)計(jì)模式的應(yīng)用驴剔,我們只需要依葫蘆畫(huà)瓢就可以創(chuàng)建所需的UI頁(yè)
閱讀上面的代碼省古,可以跑出一下問(wèn)題:
1) build方法需要一個(gè)BuildContext參數(shù),那么這個(gè)BuildContext是什么丧失?
2)build方法是模板方法豺妓,那么什么時(shí)候調(diào)用的呢?
帶著這兩個(gè)問(wèn)題布讹,后面簡(jiǎn)單的梳理下Widget的結(jié)構(gòu)琳拭,之所以說(shuō)是簡(jiǎn)單的梳理,因?yàn)殡y得我也不會(huì)描验,還沒(méi)研究到白嘁。
StatelessWidget和StatefulWidget都繼承于Widget,其定義如下:
abstract class Widget extends DiagnosticableTree {
const Widget({ this.key });
final Key key;
@protected
Element createElement();
}
Widget繼承于DiagnosticableTree,且提供了一個(gè)createElement抽象方法返回了一個(gè)Element對(duì)象膘流,該對(duì)象查看源碼可知其繼承解構(gòu)是Element extends DiagnosticableTree implements BuildContext.所以其Widget 和Element的整體解構(gòu)可以用如下圖表示:
先來(lái)看看StatelessWidget的具體實(shí)現(xiàn):
abstract class StatelessWidget extends Widget {
@override
StatelessElement createElement() => StatelessElement(this);
@protected
Widget build(BuildContext context);
}
StatelessWidget實(shí)現(xiàn)了createElement方法返回了一個(gè)StatelessElement對(duì)象絮缅,且提供了一個(gè)build方法鲁沥,注意build方法的參數(shù)是BuildContext,那么這個(gè)BuildContext是不是就是StatelessElement這個(gè)對(duì)象了呢?預(yù)知答案如何先看看build是在那兒調(diào)用的盟蚣,在StatelessElement這個(gè)類(lèi)里可以找到答案黍析,其源碼如下:
class StatelessElement extends ComponentElement {
//在element中調(diào)用了widget.build方法,并將自己傳入了進(jìn)去
//所以BuildContext就是StatelessElement
@override
Widget build() => widget.build(this);
}
通過(guò)其源碼可以知道StatelessElement繼承了ComponentElement屎开,且重寫(xiě)了build方法阐枣,其調(diào)用了widget的build方法。這個(gè)build就是StatelessWidget對(duì)象(或者其子對(duì)象)奄抽,并且可以確定StatelessWidget的build方法的參數(shù)就是StatelessElement這個(gè)對(duì)象蔼两。
所以可以斷定想要知道StatelessWidget的build(BuildContext)方法什么時(shí)候調(diào)用,就需要知道StatelessElement的build()什么時(shí)候調(diào)用逞度。在StatelessElement的父類(lèi)ComponentElement的perfromReBuild方法可以得到解答:
@override
void performRebuild() {
//省略了部分代碼
Widget built = build();
//省略部分代碼
}
所以概述下來(lái)就是StatelessWidget通過(guò)build(BuildContext)方法構(gòu)建Widget是通過(guò)StatelessElement的build()方法來(lái)完成的额划。想要調(diào)用build(BuildContext)必定先通過(guò)createElement方法創(chuàng)建一個(gè)StatelessElement對(duì)象。那么有一個(gè)此處就有一個(gè)問(wèn)題了:Widget的createElement方法是神馬時(shí)候調(diào)用的呢档泽?
上面粗略的分了StatelessWidget俊戳,下來(lái)再來(lái)簡(jiǎn)略的看下StatefullWidget這個(gè)類(lèi)。
abstract class StatefulWidget extends Widget {
@override
StatefulElement createElement() => StatefulElement(this);
@protected
State createState();
}
StatefulWidget的createElement方法返回了SatefulElement馆匿,且提供了一個(gè)createState()方法抑胎,大膽猜測(cè)一下createState就是在StatefulElement里面調(diào)用的,果不其然渐北,證據(jù)如下:
StatefulElement 的構(gòu)造器:
StatefulElement(StatefulWidget widget)
///調(diào)用了createState方法
: _state = widget.createState(), super(widget) {
}
StatefulWidget需要通過(guò)createState方法創(chuàng)建一個(gè)State,State也提供了build(BuildContext)方法阿逃。另外查看StatefulElement的可以該類(lèi)也實(shí)現(xiàn)了ComponentElement的build方法:
@override
Widget build() => state.build(this);
分析到這兒StatelessWidget ,StatefulWidget和Element的關(guān)系可以用如下圖來(lái)表示:
其構(gòu)建關(guān)系的流程圖可以用如下來(lái)表示:
build(BuildContext)方法就需要先調(diào)用具體子類(lèi)的createElement方法創(chuàng)建對(duì)應(yīng)的ComponentElement對(duì)象,而后重寫(xiě)Component的build方法赃蛛。performRebuild方法又是什么時(shí)機(jī)調(diào)用的的呢恃锉?performRebuild方法在ComponentElment的mount方法和rebuild方法()方法里面都有調(diào)用,而ComponentElement的mount方法又是Flutter形成渲染樹(shù)的入口:
//mount方法形成了解析Widget,構(gòu)建渲染樹(shù)
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
_firstBuild();
}
void _firstBuild() {
//rebuild方法內(nèi)部調(diào)用了performRebuild方法呕臂。
rebuild();
}
收錄|原文地址