它提供了一套Dart API,而後在底層經過OpenGL這種跨平臺的繪製庫(內部會調用操做系統API)實現了一套代碼跨多端。因爲Dart API也是調用操做系統API,因此它的性能接近原生。html
Flutter中,一切都是Widget,當UI要發生變化時,咱們不去直接修改DOM,而是經過更新狀態,讓Flutter UI系統來根據新的狀態來從新構建UI。node
Widget只是UI元素的一個配置數據,而且一個Widget能夠對應多個Element緩存
@immutable
abstract class Widget extends DiagnosticableTree {
const Widget({ this.key });
final Key key;
@protected
Element createElement();
@override
String toStringShort() {
return key == null ? '$runtimeType' : '$runtimeType-$key';
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
}
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
}
複製代碼
canUpdate(...)
是一個靜態方法,它主要用於在Widget樹從新build
時複用舊的widget,其實具體來講,應該是:是否用新的Widget對象去更新舊UI樹上所對應的Element
對象的配置;經過其源碼咱們能夠看到,只要newWidget
與oldWidget
的runtimeType
和key
同時相等時就會用newWidget
去更新Element
對象的配置,不然就會建立新的Element
。另外Widget
類自己是一個抽象類,其中最核心的就是定義了createElement()
接口,在Flutter開發中,咱們通常都不用直接繼承Widget
類來實現一個新組件,相反,咱們一般會經過繼承StatelessWidget
或StatefulWidget
來間接繼承Widget
類來實現。StatelessWidget
和StatefulWidget
都是直接繼承自Widget
類,而這兩個類也正是Flutter中很是重要的兩個抽象類,它們引入了兩種Widget模型,接下來咱們將重點介紹一下這兩個類。bash
StatelessWidget
用於不須要維護狀態的場景,它一般在build
方法中經過嵌套其它Widget來構建UI,在構建過程當中會遞歸的構建其嵌套的Widget。咱們看一個簡單的例子:app
build
方法有一個context
參數,它是BuildContext
類的一個實例,表示當前widget在widget樹中的上下文,每個widget都會對應一個context對象(由於每個widget都是widget樹上的一個節點)。實際上,context
是當前widget在widget樹中位置中執行」相關操做「的一個句柄,好比它提供了從當前widget開始向上遍歷widget樹以及按照widget類型查找父級widget的方法。下面是在子樹中獲取父級widget的一個示例:less
lass ContextRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Context測試"),
),
body: Container(
child: Builder(builder: (context) {
// 在Widget樹中向上查找最近的父級`Scaffold` widget
Scaffold scaffold = context.findAncestorWidgetOfExactType<Scaffold>();
// 直接返回 AppBar的title, 此處其實是Text("Context測試")
return (scaffold.appBar as AppBar).title;
}),
),
);
}
}
複製代碼
和StatelessWidget
同樣,StatefulWidget
也是繼承自Widget
類,並重寫了createElement()
方法,不一樣的是返回的Element
對象並不相同;另外StatefulWidget
類中添加了一個新的接口createState()
。ide
abstract class StatefulWidget extends Widget {
const StatefulWidget({ Key key }) : super(key: key);
@override
StatefulElement createElement() => new StatefulElement(this);
@protected
State createState();
}
複製代碼
StatefulElement
間接繼承自Element
類,與StatefulWidget相對應(做爲其配置數據)。StatefulElement
中可能會屢次調用createState()
來建立狀態(State)對象。createState()
用於建立和Stateful widget相關的狀態,它在Stateful widget的生命週期中可能會被屢次調用。例如,當一個Stateful widget同時插入到widget樹的多個位置時,Flutter framework就會調用該方法爲每個位置生成一個獨立的State實例,其實,本質上就是一個StatefulElement
對應一個State實例。在widget生命週期中能夠被改變,當State被改變時,能夠手動調用其setState()
方法通知Flutter framework狀態發生改變,Flutter framework在收到消息後,會從新調用其build
方法從新構建widget樹,從而達到更新UI的目的。函數
void initState()
Widget build(BuildContext context)
void didUpdateWidget(CounterWidget oldWidget)
void deactivate()
void dispose()
void didChangeDependencies()
複製代碼
咱們運行應用並打開該路由頁面,在新路由頁打開後佈局
I/flutter ( 5436): initState
I/flutter ( 5436): didChangeDependencies
I/flutter ( 5436): build
複製代碼
能夠看到,在StatefulWidget插入到Widget樹時首先initState
方法會被調用。性能
而後咱們點擊⚡️按鈕熱重載,控制檯輸出日誌以下:
I/flutter ( 5436): reassemble
I/flutter ( 5436): didUpdateWidget
I/flutter ( 5436): build
複製代碼
能夠看到此時initState
和didChangeDependencies
都沒有被調用,而此時didUpdateWidget
被調用。
initState
:當Widget第一次插入到Widget樹時會被調用,對於每個State對象,Flutter framework只會調用一次該回調,因此,一般在該回調中作一些一次性的操做,如狀態初始化、訂閱子樹的事件通知等。不能在該回調中調用BuildContext.dependOnInheritedWidgetOfExactType
(該方法用於在Widget樹上獲取離當前widget最近的一個父級InheritFromWidget
,關於InheritedWidget
咱們將在後面章節介紹),緣由是在初始化完成後,Widget樹中的InheritFromWidget
也可能會發生變化,因此正確的作法應該在在build()
方法或didChangeDependencies()
中調用它。didChangeDependencies()
:當State對象的依賴發生變化時會被調用;例如:在以前build()
中包含了一個InheritedWidget
,而後在以後的build()
中InheritedWidget
發生了變化,那麼此時InheritedWidget
的子widget的didChangeDependencies()
回調都會被調用。典型的場景是當系統語言Locale或應用主題改變時,Flutter framework會通知widget調用此回調。build()
:此回調讀者如今應該已經至關熟悉了,它主要是用於構建Widget子樹的,會在以下場景被調用:
initState()
以後。didUpdateWidget()
以後。setState()
以後。didChangeDependencies()
以後。reassemble()
:此回調是專門爲了開發調試而提供的,在熱重載(hot reload)時會被調用,此回調在Release模式下永遠不會被調用。didUpdateWidget()
:在widget從新構建時,Flutter framework會調用Widget.canUpdate
來檢測Widget樹中同一位置的新舊節點,而後決定是否須要更新,若是Widget.canUpdate
返回true
則會調用此回調。正如以前所述,Widget.canUpdate
會在新舊widget的key和runtimeType同時相等時會返回true,也就是說在在新舊widget的key和runtimeType同時相等時didUpdateWidget()
就會被調用。deactivate()
:當State對象從樹中被移除時,會調用此回調。在一些場景下,Flutter framework會將State對象從新插到樹中,如包含此State對象的子樹在樹的一個位置移動到另外一個位置時(能夠經過GlobalKey來實現)。若是移除後沒有從新插入到樹中則緊接着會調用dispose()
方法。dispose()
:當State對象從樹中被永久移除時調用;一般在此回調中釋放資源。StatefulWidget生命週期如圖3-2所示:
Flutter提供了一套豐富、強大的基礎組件,在基礎組件庫之上Flutter又提供了一套Material風格(Android默認的視覺風格)和一套Cupertino風格(iOS視覺風格)的組件庫。要使用基礎組件庫,須要先導入:
import 'package:flutter/widgets.dart';
複製代碼
下面咱們介紹一下經常使用的組件。
Text
:該組件可以讓您建立一個帶格式的文本。Row
、 Column
: 這些具備彈性空間的佈局類Widget可以讓您在水平(Row)和垂直(Column)方向上建立靈活的佈局。其設計是基於Web開發中的Flexbox佈局模型。Stack
: 取代線性佈局 (譯者語:和Android中的FrameLayout
類似),Stack
容許子 widget 堆疊, 你可使用 Positioned
來定位他們相對於Stack
的上下左右四條邊的位置。Stacks是基於Web開發中的絕對定位(absolute positioning )佈局模型設計的。Container
: Container
可以讓您建立矩形視覺元素。container 能夠裝飾一個BoxDecoration
, 如 background、一個邊框、或者一個陰影。 Container
也能夠具備邊距(margins)、填充(padding)和應用於其大小的約束(constraints)。另外, Container
可使用矩陣在三維空間中對其進行變換。Flutter提供了一套豐富的Material組件,它能夠幫助咱們構建遵循Material Design設計規範的應用程序。Material應用程序以MaterialApp
組件開始, 該組件在應用程序的根部建立了一些必要的組件,好比Theme
組件,它用於配置應用的主題。 是否使用MaterialApp
徹底是可選的,可是使用它是一個很好的作法。在以前的示例中,咱們已經使用過多個Material 組件了,如:Scaffold
、AppBar
、FlatButton
等。要使用Material 組件,須要先引入它:
import 'package:flutter/material.dart';
複製代碼
Flutter也提供了一套豐富的Cupertino風格的組件,儘管目前尚未Material 組件那麼豐富,可是它仍在不斷的完善中。值得一提的是在Material 組件庫中有一些組件能夠根據實際運行平臺來切換表現風格,好比MaterialPageRoute
,在路由切換時,若是是Android系統,它將會使用Android系統默認的頁面切換動畫(從底向上);若是是iOS系統,它會使用iOS系統默認的頁面切換動畫(從右向左)。因爲在前面的示例中尚未Cupertino組件的示例,下面咱們實現一個簡單的Cupertino組件風格的頁面:
//導入cupertino widget庫
import 'package:flutter/cupertino.dart';
class CupertinoTestRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text("Cupertino Demo"),
),
child: Center(
child: CupertinoButton(
color: CupertinoColors.activeBlue,
child: Text("Press"),
onPressed: () {}
),
),
);
}
}
複製代碼
abstract class StatelessWidget extends Widget Containing class: StatelessWidget
A widget that does not require mutable state. A stateless widget is a widget that describes part of the user interface by building a constellation of other widgets that describe the user interface more concretely. The building process continues recursively until the description of the user interface is fully concrete (e.g., consists entirely of RenderObjectWidgets, which describe concrete RenderObjects). www.youtube.com/watch?v=wE7khGHVkYY Stateless widget are useful when the part of the user interface you are describing does not depend on anything other than the configuration information in the object itself and the BuildContext in which the widget is inflated. For compositions that can change dynamically, e.g. due to having an internal clock-driven state, or depending on some system state, consider using StatefulWidget. Performance considerations The build method of a stateless widget is typically only called in three situations: the first time the widget is inserted in the tree, when the widget's parent changes its configuration, and when an InheritedWidget it depends on changes. If a widget's parent will regularly change the widget's configuration, or if it depends on inherited widgets that frequently change, then it is important to optimize the performance of the build method to maintain a fluid rendering performance. There are several techniques one can use to minimize the impact of rebuilding a stateless widget: Minimize the number of nodes transitively created by the build method and any widgets it creates. For example, instead of an elaborate arrangement of Rows, Columns, Paddings, and SizedBoxes to position a single child in a particularly fancy manner, consider using just an Align or a CustomSingleChildLayout. Instead of an intricate layering of multiple Containers and with Decorations to draw just the right graphical effect, consider a single CustomPaint widget. Useconst
widgets where possible, and provide aconst
constructor for the widget so that users of the widget can also do so. Consider refactoring the stateless widget into a stateful widget so that it can use some of the techniques described at StatefulWidget, such as caching common parts of subtrees and using GlobalKeys when changing the tree structure. If the widget is likely to get rebuilt frequently due to the use of InheritedWidgets, consider refactoring the stateless widget into multiple widgets, with the parts of the tree that change being pushed to the leaves. For example instead of building a tree with four widgets, the inner-most widget depending on the Theme, consider factoring out the part of the build function that builds the inner-most widget into its own widget, so that only the inner-most widget needs to be rebuilt when the theme changes. {@tool snippet} The following is a skeleton of a stateless widget subclass called GreenFrog. Normally, widgets have more constructor arguments, each of which corresponds to a final property. class GreenFrog extends StatelessWidget { const GreenFrog({ Key key }) : super(key: key);@override Widget build(BuildContext context) { return Container(color: const Color(0xFF2DBD3A)); } } {@end-tool} {@tool snippet} This next example shows the more generic widget Frog which can be given a color and a child: class Frog extends StatelessWidget { const Frog({ Key key, this.color = const Color(0xFF2DBD3A), this.child, }) : super(key: key);
final Color color; final Widget child;
@override Widget build(BuildContext context) { return Container(color: color, child: child); } } {@end-tool} By convention, widget constructors only use named arguments. Named arguments can be marked as required using @required. Also by convention, the first argument is key, and the last argument is child, children, or the equivalent. See also: StatefulWidget and State, for widgets that can build differently several times over their lifetime. InheritedWidget, for widgets that introduce ambient state that can be read by descendant widgets.
提供了一種數據在widget樹中從上到下傳遞、共享的方式,好比咱們在應用的根widget中經過InheritedWidget
共享了一個數據,那麼咱們即可以在任意子widget中來獲取該共享的數據!如Flutter SDK中正是經過InheritedWidget來共享應用主題(Theme
)和Locale (當前語言環境)信息的。
特性 didChangeDependencies
state中didChangeDependencies的回調方法,再依賴發生變化的時候會進行被調用;依賴的行爲只指子的widget是否使用了父widget中InheritedWidget 的數據;若是使用了就表明依賴了,依賴其的子widget的didChangeDependencies
方法將會被調用。
context.getElementForInheritedWidgetOfExactType<ShareDataWidget>().widget
複製代碼
若是是經過 'getElementForInheritedWidgetOfExactType'
何時使用