Flutter 第一個(gè)項(xiàng)目是一個(gè)計(jì)數(shù)器
Flutter 的Dart代碼是寫在lib 文件夾下畴椰,而 lib/main.dart 是項(xiàng)目的入口,打開main.dart 掸绞,下邊是代碼:
//引入material UI庫,可以理解為iOS的UIKit
import 'package:flutter/material.dart';
// 傳說中的main 函數(shù),使用dart 的特有的 => 調(diào)用方法
// 調(diào)用了MyApp函數(shù)
void main() => runApp(new MyApp());
// Widget(部件)捍歪,可以理解為iOS中的UIView,iOS中叫控件鸵钝,都由view封裝而成糙臼,F(xiàn)lutter中都叫部件(小部件),都由widget封裝而成
// extends:繼承(OC不這么寫恩商,因?yàn)镺C 一個(gè)類有2個(gè)文件組成 .h和.m)--OC 是最NB的[偷笑]变逃!
class MyApp extends StatelessWidget {
//重寫 build 方法(描述如何構(gòu)建UI界面)
@override
Widget build(BuildContext context) {
// MaterialApp :是Material庫中提供的Flutter APP框架,通過它可以設(shè)置應(yīng)用的名稱怠堪、主題揽乱、語言、首頁及路由列表等
return new MaterialApp(
//名稱
title: 'Flutter Demo',
//主題
theme: new ThemeData(
primarySwatch: Colors.blue,
),
//應(yīng)用首頁路由
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
//首頁
class MyHomePage extends StatefulWidget {
//可選參數(shù)粟矿,首頁導(dǎo)航標(biāo)題凰棉,如果不清楚可以
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
// 加載首頁的狀態(tài)類,StatefulWidget 的特殊寫法陌粹,下邊詳細(xì)介紹
@override
_MyHomePageState createState() => new _MyHomePageState();
}
// State 狀態(tài)
// <MyHomePage> 這這個(gè)類的狀態(tài)
class _MyHomePageState extends State<MyHomePage> {
//記錄計(jì)數(shù)器的數(shù)字
int _counter = 0;
// 點(diǎn)擊 “+” 按鈕調(diào)用的方法撒犀,實(shí)現(xiàn) i++
// _incrementCounter 這種下劃線的命名的方法,表示是一個(gè)私有方法
void _incrementCounter() {
// 刷新頁面
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
// Scaffold :是Material庫中提供的頁面腳手架掏秩,它包含導(dǎo)航欄和Body以及FloatingActionButton
// 就是一個(gè)封裝了導(dǎo)航欄等等功能的小部件绘证,可以簡單理解為UINavgation
return new Scaffold(
// 導(dǎo)航欄
appBar: new AppBar(
title: new Text(widget.title),
),
// Center : 一個(gè)空白的view (web 中的 div )
body: new Center(
// Column: 一種布局方案,下一章布局詳細(xì)介紹
child: new Column(
// 布局中的對齊哗讥,居中
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// lab
new Text(
'You have pushed the button this many times:',
),
new Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
// 按鈕
floatingActionButton: new FloatingActionButton(
onPressed: _incrementCounter,// 調(diào)用方法
tooltip: 'Increment',
child: new Icon(Icons.add),// icon
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
代碼還是比較簡單的嚷那,點(diǎn)擊按鈕,調(diào)用 _incrementCounter 方法杆煞,_counter++魏宽,刷新頁面腐泻,讓 Text 顯示
接下來,詳細(xì)介紹一下队询,里邊包含的知識點(diǎn):
StatelessWidget 和 StatefulWidget
StatelessWidget: 無狀態(tài)Widget派桩,一旦創(chuàng)建,不能改變
StatefulWidget: 有狀態(tài)的Widget蚌斩,可以根據(jù)狀態(tài)改變
- StatefulWidget 其實(shí)就是 StatelessWidget + 狀態(tài)組成
- 其實(shí) StatefulWidget 能夠改變铆惑,也只是保存了“狀態(tài)”,刪除了老的StatelessWidget送膳,重新創(chuàng)建了一個(gè)新的
- 所以StatelessWidget 是一個(gè)方法员魏,而StatefulWidget 要多寫一個(gè) State 方法
刷新頁面 setState
// 下邊2種寫法都可以
_counter++;
setState(() {});
setState(() {
_counter++;
});
- 調(diào)用刷新頁面,刪除了老的StatelessWidget叠聋,新建一個(gè)StatelessWidget撕阎,重新展示改變狀態(tài)以后的頁面
- 代碼邏輯其實(shí)重新執(zhí)行build 方法,來重繪碌补,所以build 方法里邊只放和頁面布局相關(guān)的東西虏束,不要放邏輯代碼
widget 底層
widget 繼承DiagnosticableTree,主要的包括key厦章,Element 镇匀,canUpdate等等。
- key:這個(gè)key屬性類似于React/Vue中的key袜啃,主要的作用是決定是否在下一次build時(shí)復(fù)用舊的widget汗侵,決定的條件在canUpdate()方法中。
- canUpdate:是否用新的Widget對象去更新舊UI樹上所對應(yīng)的Element對象的配置囊骤;只要newWidget與oldWidget的runtimeType和key同時(shí)相等時(shí)就會用newWidget去更新Element對象的配置晃择,否則就會創(chuàng)建新的Element。
- Element屬性
事實(shí)上 Widget 只是 Element 的一個(gè)配置描述 也物,告訴 Element 這個(gè)實(shí)例如何去渲染宫屠。
從上圖注釋也可知: Widget 和 Element 之間是一對多的關(guān)系 。
而Element 里邊有renderObject屬性
而renderObject 是Flutter布局和繪制的基本單元
由此可以得出: Widget 生成了 Element滑蚯,而后創(chuàng)建 RenderObject 關(guān)聯(lián)到 Element 的內(nèi)部 renderObject 對象上浪蹂,最后Flutter 通過 RenderObject 數(shù)據(jù)來布局和繪制。
說到 RenderObject 告材,就不得不說 RenderBox 坤次,從源碼注釋可以看出,它是在繼承 RenderObject 基礎(chǔ)的布局和繪制功能上斥赋,實(shí)現(xiàn)了“笛卡爾坐標(biāo)系”:以 Top缰猴、Left 為基點(diǎn),通過寬高兩個(gè)軸實(shí)現(xiàn)布局和嵌套的疤剑。
RenderBox 避免了直接使用 RenderObject 的麻煩場景滑绒,其中 RenderBox 的布局和計(jì)算大小是在 performLayout() 和 performResize() 這兩個(gè)方法中去處理闷堡,很多時(shí)候我們更多的是選擇繼承 RenderBox 去實(shí)現(xiàn)自定義。
綜合上述情況疑故,我們知道:
- Widget只是顯示的數(shù)據(jù)配置杠览,所以相對而言是輕量級的存在,而 Flutter 中對 Widget 的也做了一定的優(yōu)化纵势,所以每次改變狀態(tài)導(dǎo)致的 Widget 重構(gòu)并不會有太大的問題踱阿。
- RenderObject 就不同了,RenderObject 涉及到布局钦铁、計(jì)算软舌、繪制等流程,要是每次都全部重新創(chuàng)建開銷就比較大了育瓜。
所以針對是否每次都需要?jiǎng)?chuàng)建出新的 Element 和 RenderObject 對象葫隙,Widget 都做了對應(yīng)的判斷以便于復(fù)用栽烂,比如:在 newWidget 與oldWidget 的 runtimeType 和 key 相等時(shí)會選擇使用 newWidget 去更新已經(jīng)存在的 Element 對象躏仇,不然就選擇重新創(chuàng)建新的 Element。
由此可知:Widget 重新創(chuàng)建腺办,Element 樹和 RenderObject 樹并不會完全重新創(chuàng)建焰手。
那么再來個(gè)圖理解一下:
widget 底層這塊參考大神所寫:
https://juejin.im/post/5c7e853151882549664b0543