本文主要介紹 Flutter 應用程式中 Widget
,State
,Context
和 InheritedWidget
等重要概念。 尤其注意的是 InheritedWidget
,它是最重要且介紹較少的 Widget
之一。
前言
每個 Flutter
開發人員都需要完全理解 Widget
,State
和 Context
概念。
雖然,有很多文件可以查詢,但想要清晰地解釋這些概念,還是有一定難度。
本文會對這些概念進行剖析,力圖使你能夠進一步瞭解這些概念:
- 有狀態和無狀態
Widegt
的區別 Context
是什麼State
是什麼以及如何使用它Context
與其State
物件之間的關係InheritedWidget
以及在Widgets
樹中傳遞資訊的方式rebuild
概念
第一部分:概念
Widget
在 Flutter
中,一切都是 Widget
。
將 Widget
視為視覺化元件(或視覺化互動的元件)。
當您需要構建直接或間接佈局關係時,您正在使用 Widget
。
Widget 樹
Widget
是以樹結構組織起來的。
包含其他 Widget
的 Widget
稱為父 Widget
(或 Widget
容器)。包含在父 Widget
中的 Widget
稱為子 Widget
。
以 Flutter
自動生成的應用程式來進行說明。 構建程式碼:
@overrideWidget build(BuildContext){
return new Scaffold( appBar: new AppBar( title: new Text(widget.title), ), body: new Center( child: new Column( mainAxisAlignment: MainAxisAlignment.center, children: <
Widget>
[ new Text( 'You have pushed the button this many times:', ), new Text( '$_counter', style: Theme.of(context).textTheme.display1, ), ], ), ), floatingActionButton: new FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: new Icon(Icons.add), ), );
}複製程式碼
我們將獲得以下 Widget
樹(僅限程式碼中存在的 Widget
列表):
Context 上下文
另一個重要的概念是 Context
。
Context
是構建所有 Widgets
樹結構中的 Widget
的位置引用。
簡而言之,將
Context
視為Widgets
樹的一部分。
一個 Context
僅屬於一個 Widget
。同樣具有父子關係,如果 Widget A
具有子 Widgets
,則 Widget A
的 Context
將成為子 Widget
的 Context
的父 Context
。
很明顯,Context
是關聯在一起,組成一個 Context
樹(父子關係)。
現在,我們使用上圖說明 Context
的概念,每種顏色代表一個 Context
(除了 MyApp
,它是不同的):
Context
可見性:某些屬性只能在其自己的Context
中或在其父Context
中可見。
使用這種語法,我們可以從子 Context
追根溯源,很容易找到一個祖先(或者父)Widget
。
舉例,考慮
Scaffold >
通過從
Center >
Column >
Text:context.ancestorWidgetOfExactType(Scaffold)=>Text Context
向上查詢到頂部Scaffold
。
從父 Context
中,也可以找到後代(=子) Widget
,但不建議這樣做(我們稍後會討論)。
兩種 Widgets
無狀態 StatelessWidget
這些可視元件,只依賴它們自己的配置資訊,並不依賴於任何外部資訊。這些配置資訊在構建時由其父元件提供。
換句話說,這些 Widgets
一旦建立就不關心任何變化。
這些 Widgets
被稱為無狀態 Widgets
。
這些 Widgets
的典型示例是 Text,Row,Column,Container ......
在構建時,我們只是將一些引數傳遞給它們。
引數可以是文字,尺寸甚至其他 Widgets
。 唯一重要的是這個配置資訊一旦應用,在下一個構建過程之前不會改變。
無狀態
Widget
只能在載入或者構建Widget
時繪製一次,這意味著無法基於任何事件或使用者操作重繪該Widget
。
StatelessWidget 生命週期
無狀態 Widget
相關的程式碼的典型結構如下。
如您所見,我們可以將一些額外的引數傳遞給它的建構函式。 但是,請記住,這些引數不會在以後階段發生變化,只能按原樣使用。
class MyAppStatelessWidget extends StatelessWidget {
MyAppStatelessWidget({
Key key, this.parameter,
}): super(key:key);
final parameter;
@override Widget build(BuildContext context){
return new ...
}
}複製程式碼
StatelessWidget
的另一個方法 createElement
也可以被複寫,但你幾乎不會這樣做。 唯一需要被複寫的是 · build
函式。
這種無狀態 Widget
的生命週期很簡單:
- 初始化
- 通過
build
進行渲染
有狀態 StatefulWidget
某些 Widge
需要處理一些在 Widget
生命週期內會發生變化的內部資料。
這些 Widget
儲存的資料集在 Widget
的生命週期中可能會有所不同,這樣的資料集被稱為為 State
。
這樣的 Widget
被稱為有狀態 Widget
。
此類 Widget
的示例可以是核取方塊列表,也可以是根據條件禁用的 Button
。
State
State
定義 StatefulWidget
例項的 行為
部分。
它包含 Widget
互動 的資訊:
- 行為
- 佈局
施加於 State 的任何更改都會強制
Widget
重建。
State 與 Context 之間的關係
對於有狀態 Widgets
,State
與 Context
相關聯。 此關聯是永久性的,State
物件永遠不會更改其 Context
。
即使可以在 Widgets
樹內移動 Widget
Context
,State
仍將與該 Context
相關聯。
當 State
與 Context
關聯時,State
被視為已掛載。
至關重要:
由於
State
物件與Context
相關聯,這意味著State
物件不能(直接)通過另一個Context
訪問! (我們將在稍後討論這個問題)。
有狀態 Widget 生命週期
基本概念已經介紹過了,是時候深入瞭解了。
由於本文的主要意圖是用 “變數” 資料來解釋 State
的概念,因此會故意跳過某些與 Stateful Widget
可複寫方法相關的任何解釋,這些方法與此沒有特別的關係。 這些可複寫的方法是 didUpdateWidget
,deactivate
,reassemble
。
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key, this.title
}) : super(key: key);
final String title;
@override _MyStatefulWidgetState createState() =>
_MyStatefulWidgetState();
}class _MyStatefulWidgetState extends State<
MyStatefulWidget>
{
@override void initState() {
// TODO: implement initState super.initState();
} @override void didChangeDependencies() {
// TODO: implement didChangeDependencies super.didChangeDependencies();
} @override void dispose() {
// TODO: implement dispose super.dispose();
} @override Widget build(BuildContext context) {
return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <
Widget>
[ Text( 'You have pushed the button this many times:', ), Text( '', style: Theme.of(context).textTheme.display1, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: (){
}, tooltip: 'Increment', child: Icon(Icons.add), ), // This trailing comma makes auto-formatting nicer for build methods. );
}
}複製程式碼
下面時序圖顯示了建立有狀態 Widget
相關的操作。 在圖的右側,標註了 State
物件的內部狀態。 同時您可以看 Context
與 State
關聯的時刻,從而變為有效的(掛載)。
initState()
initState()
方法是在建立 State
物件後要呼叫的第一個方法(在建構函式之後)。需要執行自定義初始化內容時,需要複寫此方法。 通常初始化,動畫,邏輯控制等。如果重寫此方法,需要先呼叫 super.initState()
方法。
在這個方法中,Context 可用但你還不能真正使用它,因為框架還沒有完全將 State
與它相關聯。
一旦 initState()
方法執行完成,State
物件完成初始化並且 Context
可用。
在State
物件的生命週期內,不會再呼叫 initState()
方法。即 initState()
只被呼叫一次。
didChangeDependencies()
didChangeDependencies()
方法是要呼叫的第二個方法。
在此階段,由於 Context
可用,您可以使用它。
如果您的 Widget
連結到 InheritedWidget
,並且需要初始化某些偵聽器(基於 Context
),則需要複寫此方法。
請注意,如果您的 Widget
關聯到 InheritedWidget
,則每次重建此 Widget
時都會呼叫此方法。
如果重寫此方法,則應首先呼叫 super.didChangeDependencies()
。
build()
build(BuildContext context)
方法在 didChangeDependencies()
(和didUpdateWidget
)之後呼叫。
這是您構建 Widgets
(可能還有任何子樹)的地方。
每次 State
物件更改時(或者當 InheritedWidget
需要通知 “已註冊” 的 Widget
時)都會呼叫此方法!
如果要主動重建,您可以呼叫 setState ( () {...
方法。
})
dispose()
當 Widget
廢棄時呼叫 dispose()
方法。
如果需要執行一些清理(例如偵聽器)工作,需要複寫此方法,之後呼叫 super.dispose()
。
有狀態和無狀態 Widget 如何選擇
Widget無狀態或有狀態?如何選擇。
為了回答這個問題,請問問自己:
在我的
Widget
生命週期中,是否需要考慮一個變數, 將要更改,何時更改,從而強制重建Widget
?
如果問題的答案是肯定的,那麼您需要一個有狀態 Widget
,否則,您需要一個無狀態 Widget
。
舉例:
-
用於顯示覆選框列表的
Widget
。要顯示覆選框,您需要考慮一系列item
。每個item
都是一個具有title
和 ‘status’ 的物件。如果單擊核取方塊,則會切換相應的item.status
。在這種情況下,您需要使用有狀態
Widget
來記住專案的狀態,以便能夠重繪核取方塊。 -
單一表格, 該表格
Widget
允許使用者輸入,並將輸入之後的表格傳送到伺服器。在這種情況下,除非您需要在提交表單之前驗證表單或執行任何其他操作,否則無狀態
Widget
可能就足夠了。
有狀態 Widget 由兩部分組成
Widget 定義
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key, this.title
}) : super(key: key);
final String title;
@override _MyStatefulWidgetState createState() =>
_MyStatefulWidgetState();
}複製程式碼
第一部分 MyStatefulWidget
通常是 Widget
的公共部分。 當您要將其新增到 Widget
樹,可以例項化此Widget
。此部分在 Widget
的生命週期內不會發生變化,但可以接受其相應的 State
例項引數。
請注意,在 Widget
的第一部分定義的任何變數通常在其生命週期內不會更改。
State
定義
class _MyStatefulWidgetState extends State<
MyStatefulWidget>
{
... @override Widget build(BuildContext context){
...
}
}複製程式碼
第二部分 _MyStatefulWidgetState
是在 Widget
的生命週期中變化的部分,並在每次應用修改時強制重建 Widget
例項。 名稱開頭的 _
字元表示該類對當前 .dart
檔案是私有的。
如果需要在 .dart
檔案之外引用此類,去掉 _
字首即可。
_MyStatefulWidgetState
類可以使用 widget.{變數名
訪問儲存在
}MyStatefulWidget
中的任何變數。 示例:widget.color
Widget 的唯一標識 – Key
在Flutter中,每個Widget都是唯一標識的。這個唯一標識由框架在構建/渲染時定義。
此唯一標識對應為可選的 Key
引數。 如果省略,Flutter將為您生成一個。
在某些情況下,您可能需要使用此 Key
,以便可以通過 Key
訪問 Widget
。
為此,您可以使用以下類之一:GlobalKey
,LocalKey
,UniqueKey
或 ObjectKey
。
GlobalKey確保 Key
在整個應用程式中是唯一的。
使用 Widget
的唯一標識 Key
:
GlobalKey myKey = new GlobalKey();
... @override Widget build(BuildContext context){
return new MyWidget( key: myKey );
}複製程式碼
第二部分, 如何訪問 State
如前所述,State 關聯一個 Context
,Context
關聯一個 Widget
例項。
1. Widget 本身
從理論上講,唯一能夠訪問狀態的是 Widget State
本身。
在這種情況下,沒有困難。 Widget State
類訪問其任何內部變數。
2. 子 Widget
有時候,父 Widget
可能需要根據其子 Widget
的狀態執行特定任務。
在Flutter中,每個Widget都有一個唯一的標識,由框架在構建/渲染時確定。
如前所示,您可以使用 key
引數強制標識 Widget
,
... GlobalKey<
MyStatefulWidgetState>
myWidgetStateKey = new GlobalKey<
MyStatefulWidgetState>
();
... @override Widget build(BuildContext context){
return new MyStatefulWidget( key: myWidgetStateKey, color: Colors.blue, );
}複製程式碼
一旦確定,父 Widget
可以通過以下方式訪問其子 Widget
的狀態:
myWidgetStateKey.currentState
讓我們考慮一個基本示例,當使用者點選按鈕時顯示 SnackBar
。 由於 SnackBar
是 Scaffold
的子 Widget
,它不能直接訪問 Scaffold
的任何其他子 Widget
。 因此,訪問它的唯一方法是 ScaffoldState
,公開一個公共方法來顯示 SnackBar
。
class _MyScreenState extends State<
MyScreen>
{
/// the unique identity of the Scaffold final GlobalKey<
ScaffoldState>
_scaffoldKey = new GlobalKey<
ScaffoldState>
();
@override Widget build(BuildContext context){
return new Scaffold( key: _scaffoldKey, appBar: new AppBar( title: new Text('My Screen'), ), body: new Center( new RaiseButton( child: new Text('Hit me'), onPressed: (){
_scaffoldKey.currentState.showSnackBar( new SnackBar( content: new Text('This is the Snackbar...'), ) );
} ), ), );
}
}複製程式碼
3. Widget 祖先
1. 帶狀態的 Widget
(紅色)需要暴露其狀態
為了公開它的狀態,Widget需要在建立時記錄它,如下所示:
class MyExposingWidget extends StatefulWidget {
MyExposingWidgetState myState;
@override MyExposingWidgetState createState(){
myState = new MyExposingWidgetState();
return myState;
}
}複製程式碼
2. Widget State
需要暴露 getters/setters
為了讓其他 Widget
可以 設定/獲取 State屬性,Widget State
需要通過以下方式授權訪問:
- public 屬性 (不推薦)
- getter / setter
例子:
class MyExposingWidgetState extends State<
MyExposingWidget>
{
Color _color;
Color get color =>
_color;
...
}複製程式碼
3. 藍色 Widget
獲得 State 引用
class MyChildWidget extends StatelessWidget {
@override Widget build(BuildContext context){
final MyExposingWidget widget = context.ancestorWidgetOfExactType(MyExposingWidget);
final MyExposingWidgetState state = widget?.myState;
return new Container( color: state == null ? Colors.blue : state.color, );
}
}複製程式碼
這個解決方案很容易實現,但子 Widget
無法知道它何時需要重建,
它必須等待重建才能重新整理其內容,這將導致UI無法及時重新整理。
下一節將討論 Inherited Widget
概念,它可以解決這個問題。
InheritedWidget
簡而言之,InheritedWidget
允許在 Widget
樹中有效地傳遞(和共享)資訊。
InheritedWidget
是一個特殊的 Widget
,您可以將其插入 Widget
樹中,作為一個 Widget
子樹的父節點。 該子樹的所有 Widget
都能夠訪問 InheritedWidget
公開的資料。
基礎知識
為了解釋 InheritedWidget
,讓我們考慮以下程式碼:
class MyInheritedWidget extends InheritedWidget {
MyInheritedWidget({
Key key, @required Widget child, this.data,
}): super(key: key, child: child);
final data;
static MyInheritedWidget of(BuildContext context) {
return context.inheritFromWidgetOfExactType(MyInheritedWidget);
} @override bool updateShouldNotify(MyInheritedWidget oldWidget) =>
data != oldWidget.data;
}複製程式碼
此程式碼定義了一個名為 MyInheritedWidget
的 Widget,目的是 “共享” 所有 Widget
中的某些資料。
如前所述,為了能夠傳遞/共享某些資料,需要將 InheritedWidget
定位在 Widget
樹的頂部,這解釋為什麼 InheritedWidget
建構函式設定 Widget
為 @required
。
靜態方法 MyInheritedWidget(BuildContext context)
允許所有子 Widget
獲取最近包含 Context
的 MyInheritedWidget
例項(參見後面的內容)。
最後重寫 updateShouldNotify
方法,用於設定 InheritedWidget
是否必須通知所有子 Widget
(已註冊/已訂閱),如果資料發生修改(請參閱下文)。
因此,我們需要將它放在樹節點上,如下所示:
class MyParentWidget... {
... @override Widget build(BuildContext context){
return new MyInheritedWidget( data: counter, child: new Row( children: <
Widget>
[ ... ], ), );
}
}複製程式碼
子 Widget
如何訪問 InheritedWidget
的資料
在構建子 Widget
時,將獲得 InheritedWidget
的引用,如下所示:
class MyChildWidget... {
... @override Widget build(BuildContext context){
final MyInheritedWidget inheritedWidget = MyInheritedWidget.of(context);
/// /// From this moment, the widget can use the data, exposed by the MyInheritedWidget /// by calling: inheritedWidget.data /// return new Container( color: inheritedWidget.data.color, );
}
}複製程式碼
如何在 Widget
之間進行互動 ?
思考下面 Widget
樹。
為了說明一種互動方式,我們假設如下:
Widget A
是一個按鈕,點選時將貨物新增到購物車;Widget B
是一個文字,顯示購物車中商品數量;Widget C
位於Widget B
旁邊,是一個文字;- 我們希望在按下
Widget A
時自動在Widget B
購物車中顯示正確數量的專案,但我們不希望重建Widget C
,InheritedWidget
應用場景正式於此!
main.dart:
import 'package:flutter/material.dart';
import 'package:flutter_app_demo/inherited.dart';
import 'package:flutter_app_demo/widgetA.dart';
import 'package:flutter_app_demo/widgetB.dart';
import 'package:flutter_app_demo/widgetC.dart';
void main() =>
runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application. @override Widget build(BuildContext context) {
return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyStatefulWidget(title: 'Flutter Demo Home Page'), );
}
}class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key, this.title
}) : super(key: key);
final String title;
@override _MyStatefulWidgetState createState() =>
_MyStatefulWidgetState();
}class _MyStatefulWidgetState extends State<
MyStatefulWidget>
{
@override Widget build(BuildContext context) {
return MyInheritedWidget( child: Scaffold( appBar: AppBar( title: Text('I am Tree'), ), body: Column( children: <
Widget>
[ WidgetA(), Padding(padding: EdgeInsets.all(15.0),), Container( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <
Widget>
[ WidgetB(), WidgetC(), ], ), ) ], ), ), );
}
}複製程式碼
inherited.dart
import 'package:flutter/material.dart';
class MyInheritedWidget extends StatefulWidget{
MyInheritedWidget({Key key, this.child,
}):super(key: key);
final Widget child;
MyInheritedWidgetState createState() =>
new MyInheritedWidgetState();
static MyInheritedWidgetState of(BuildContext context){
return (context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited).data;
}
}class MyInheritedWidgetState extends State<
MyInheritedWidget>
{
List<
Item>
_items = <
Item>
[];
int get itemsCount =>
_items.length;
void addItem(String name){
setState(() {
_items.add(new Item(name));
});
} @override Widget build(BuildContext context) {
return new _MyInherited(child: widget.child, data: this);
}
}class _MyInherited extends InheritedWidget {
_MyInherited({
Key key, @required Widget child, @required this.data,
}) : super(key: key, child: child);
final MyInheritedWidgetState data;
@override bool updateShouldNotify(_MyInherited oldWidget) {
return true;
}
}class Item{
String name;
Item(this.name);
}複製程式碼
Widgets:
class WidgetA extends StatelessWidget {
@override Widget build(BuildContext context) {
final MyInheritedWidgetState state = MyInheritedWidget.of(context);
return new Container( child: new RaisedButton( child: new Text('WidgetA Add Item', textAlign: TextAlign.right, overflow: TextOverflow.ellipsis, style: TextStyle( color: Colors.blue, ), ), onPressed: () {
state.addItem('new item');
}, ), );
}
}class WidgetB extends StatelessWidget{
@override Widget build(BuildContext context) {
final MyInheritedWidgetState state = MyInheritedWidget.of(context);
return new Container( child: Row( children: <
Widget>
[ Icon(Icons.shopping_cart), Text('${state.itemsCount
}', textAlign: TextAlign.right, overflow: TextOverflow.ellipsis, style: TextStyle( color: Colors.red, ), ), ], ), );
}
}class WidgetC extends StatelessWidget{
@override Widget build(BuildContext context) {
return new Text( 'Widget C', textAlign: TextAlign.right, overflow: TextOverflow.ellipsis, style: TextStyle(// fontWeight: FontWeight.bold, color: Colors.green[200], ), );
}
}複製程式碼
解釋說明
在這個非常基本的例子中,
_MyInherited
是一個InheritedWidget
,每次我們點選Widget A
按鈕新增一個Item
時都會被重新建立。MyInheritedWidget
是一個Widget
,其狀態包含Items
列表。 可以通過MyInheritedWidgetState of(BuildContext context)
靜態方法訪問此狀態。MyInheritedWidgetState
公開一個 getter(itemsCount)和一個addItem
方法,以便它們可以被Widget
使用。- 每次我們將一個
Item
新增到State
,MyInheritedWidgetState
都會重建。 MyStatefulWidget
類只是構建一個Widget
樹,將MyInheritedWidget
作為樹的根節點。WidgetA
是一個簡單的RaisedButton
,當按下它時,呼叫MyInheritedWidget
的addItem
方法。WidgetB
是一個簡單的文字,顯示MyInheritedWidget
的item
數。
這一切是如何運作 ?
註冊 Widget 以供以後通知
當子 Widget
呼叫 MyInheritedWidget.of(context)
時,它呼叫 MyInheritedWidget
的以下方法,將 ‘Context’ 作為引數進行傳遞。
static MyInheritedWidgetState of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited).data;
}複製程式碼
在內部,除了簡單地返回 MyInheritedWidgetState
的例項之外,它還向 Widget
訂閱更改通知。
在場景後面,對這個靜態方法的簡單呼叫實際上做了兩件事:
Widget
被自動新增到訂閱列表中,當對_MyInherited
進行修改時, 將自動重建。MyInheritedWidgetState
中引用的資料將返回給使用者。
由於 Widget A
和 Widget B
都已使用 InheritedWidget
訂閱,因此如果對 _MyInherited
進行修改,即當單擊 Widget A
的 RaisedButton
時,大致操作流程如下:
- 呼叫
MyInheritedWidgetState
的addItem
方法。 MyInheritedWidgetState.addItem
方法將新項新增到List
。- 呼叫
setState()
重建MyInheritedWidget
。 - 使用
List
的新內容建立_MyInherited
的新例項。 _MyInherited
記錄新State
。- 作為
InheritedWidget
,它檢查是否需要通知訂閱者(答案是需要)。 - 它遍歷整個訂閱者列表(這裡是
Widget A
和Widget B
)並要求他們重建。 - 由於
Wiget C
不是訂閱者,因此不會重建。
Widget A
和 Widget B
都重建了,而重建 Wiget A
卻沒用,因為它沒有任何改變。如何防止這種情況發生?
訪問 InheritedWidget 時阻止某些 Widget 重建
Widget A
也被重建的原因是訪問 MyInheritedWidgetState
的方式。
如前所述,呼叫 context.inheritFromWidgetOfExactType()
方法會自動將 Widget
訂閱到訂閱列表中。
防止此自動訂閱同時仍允許 Widget A
訪問 MyInheritedWidgetState
的解決方案是更改MyInheritedWidget
的靜態方法,如下所示:
static MyInheritedWidgetState of([BuildContext context, bool rebuild = true]){
return (rebuild ? context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited : context.ancestorWidgetOfExactType(_MyInherited) as _MyInherited).data;
}複製程式碼
新增額外布林引數:
- 如果
rebuild
引數為true
(預設情況下),Widget將被新增到訂閱者列表中。 - 如果
rebuild
引數為false
,我們仍然可以訪問資料,但不使用InheritedWidget
的內部實現。
因此,要完成解決方案,我們還需要稍微更新 Widget A
的程式碼,如下所示(我們新增 false
引數):
class WidgetA extends StatelessWidget {
@override Widget build(BuildContext context) {
final MyInheritedWidgetState state = MyInheritedWidget.of(context, false);
return new Container( child: new RaisedButton( child: new Text('Add Item'), onPressed: () {
state.addItem('new item');
}, ), );
}
}複製程式碼
現在,按下 button
不會重建 Widget A
.
總結
通篇,我們介紹了 Flutter
的核心概念, Widget, State, Context, InheritedWidget。
並且,完成了 State 之間互動的介紹。並使用 InheritedWidget
進行了 Demo製作。希望這篇文章,能夠幫助您,深入瞭解 Flutter
。