主頁 > 移動端開發 > 【Flutter核心類分析】深入理解Key.

【Flutter核心類分析】深入理解Key.

2021-11-11 08:32:12 移動端開發

背景

我們在進行Flutter開發程序中,幾乎每一個Widget都會有一個可選引數——Key,但是我們卻很少去傳這個值,既然我們可以不用傳,那么這個Key作用到底是什么呢?

問題

下面我們先來看看這樣一個場景:

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: BodyWidget(),
    );
  }
}

class BodyWidget extends StatefulWidget {
  const BodyWidget({Key key}) : super(key: key);

  @override
  _BodyWidgetState createState() => _BodyWidgetState();
}

class _BodyWidgetState extends State<BodyWidget> {
  List<Widget> list = [
    //上面兩個顯示StateLessColorBoxContainer
    StateLessColorBoxContainer(),
    StateLessColorBoxContainer(),
    //分割線
    Divider(
      height: 60.0,
      color: Colors.black,

    ),
		//下面兩個顯示StatefulColorBoxContainer
    StatefulColorBoxContainer(),
    StatefulColorBoxContainer(),
  ];

  void switchWidget() {
    setState(() {
      list.insert(0, list.removeAt(1));
      list.insert(3, list.removeAt(4));
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: new Text("KeyDemo"),
      ),
      body: Container(
        child: Center(
          child: Column(
            children: list,
          ),
        ),
      ),
      floatingActionButton: ElevatedButton(
          onPressed: () {
            switchWidget();
          },
          child: Text("點擊交換")),
    );
  }
}

class StateLessColorBoxContainer extends StatelessWidget {
  StateLessColorBoxContainer({Key key}) : super(key: key);

  final int random = Random().nextInt(4);
  final List<Color> colors = [
    Colors.red,
    Colors.yellow,
    Colors.green,
    Colors.grey
  ];

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100,
      height: 100,
      color: colors[random],
    );
  }
}

//定義StatefulColorBoxContainerContainer
class StatefulColorBoxContainer extends StatefulWidget {
  StatefulColorBoxContainer({Key key}) : super(key: key);

  @override
  _StatefulColorBoxContainerState createState() =>
      _StatefulColorBoxContainerState();
}

class _StatefulColorBoxContainerState extends State<StatefulColorBoxContainer> {
  int random = Random().nextInt(3);
  var colors = [Colors.red, Colors.yellow, Colors.green, Colors.grey];

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100,
      height: 100,
      color: colors[random],
    );
  }
}

image-20211109222357816

原始碼比較好理解,上面兩個顯示兩個StateLessColorBoxContainer,下面兩個顯示StatefulColorBoxContainer,兩ColorBuildContainer類的build(context)方法完全一樣,但是我們卻發現點擊下面的交換按鈕,只有上面的每次進行了交換,而下面的不論點擊多少次,都沒有進行交換,

這是為什么呢,為什么使用StatefulWidget就不能成功交換更新呢?這就需要從Widget的更新機制說起來了,

Widget更新機制

在 Flutter 框架中,視圖維持在樹的結構中,我們撰寫的 Widget 一個嵌套一個,最終組合為一個 Tree,

StatelessWidget更新機制

在第一種使用 StatelessWidget 的實作中,當 Flutter 渲染這些 Widgets 時,Row Widget 為它的子 Widget 提供了一組有序的插槽,對于每一個 Widget,Flutter 都會構建一個對應的 Element,構建的這個 Element Tree 相當簡單,僅保存有關每個 Widget 型別的資訊以及對子Widget 的參考,你可以將這個 Element Tree 當做就像你的 Flutter App 的骨架,它展示了 App 的結構,但其他資訊需要通過參考原始Widget來查找,

當我們交換行中的兩個色塊時,Flutter 遍歷 Widget 樹,看看骨架結構是否相同,它從 Row Widget 開始,然后移動到它的子 Widget,Element 樹檢查 Widget 是否與舊 Widget 是相同型別和 Key, 如果都相同的話,它會更新對新 widget 的參考,在我們這里,Widget 沒有設定 Key,所以Flutter只是檢查型別,它對第二個孩子做同樣的事情,所以 Element 樹將根據 Widget 樹進行對應的更新,

當 Element Tree 更新完成后,Flutter 將根據 Element Tree 構建一個 Render Object Tree,最終開始渲染流程,

StatefulWidget更新機制

當使用 StatefulWidget 實作時,控制元件樹的結構也是類似的,只是現在 color 資訊沒有存盤控制元件自身了,而是在外部的 State 物件中,

現在,我們點擊按鈕,交換控制元件的次序,Flutter 將遍歷 Element 樹,檢查 Widget 樹中 Row 控制元件并且更新 Element 樹中的參考,然后第一個 StatefulColorBoxContainer 控制元件檢查它對應的控制元件是否是相同型別,它發現對方是相同的型別; 然后第二個 StatefulColorBoxContainer 控制元件做相同的事情,最終就導致 Flutter 認為這兩個控制元件都沒有發生改變,Flutter 使用 Element 樹和它對應的控制元件的 State 去確定要在設備上顯示的內容, 所以 Element 樹沒有改變,顯示的內容也就不會改變,

那么如何解決這個StatefulWidget不更新的問題呢?這就需要用到Key了

StatefullWidget 結合 Key

StatefullWidget中有個方法canUpdate,我們先看下原始碼:

@immutable
abstract class Widget extends DiagnosticableTree {
  const Widget({ this.key });
  final Key key;
  ···
  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }
}

通過原始碼不難判斷出上面的例子中,所有的canUpdate回傳的都是true,也就是意味著他不回去重新創建Element,而是通過Widget配置資訊去更新Element,但是呢上文的StatefulColorBoxContainer卻沒有保存color資訊,所以導致Element也不會去更新,如果我們想要在runtimeType相同的情況下去正確的執行更新操作就只能使用到key了,每個widget如果key不同,也就是canUpdate回傳為false,那么它對于的Element也就會重建,也就能夠正確的執行交換了,

上面的例子中我們通過在StatefulColorBoxContainer加入key引數,就能正確的交換了,

  List<Widget> list = [
    StateLessColorBoxContainer(),
    StateLessColorBoxContainer(),
    //分割線
    Divider(
      height: 60.0,
      color: Colors.black,
    ),
		// 加入不同的key
    StatefulColorBoxContainer(key: UniqueKey()),
    StatefulColorBoxContainer(key: UniqueKey()),
  ];

Key的種類

Key 的目的在于為每個 Widget 指明一個唯一的身份,使用何種 Key 就要依具體的使用場景決定,

ValueKey

例如在一個 ToDo 串列應用中,每個 Todo Item 的文本是恒定且唯一的,這種情況,適合使用 ValueKey,value 是文本,

ObjectKey

假設,每個子 Widget 都存盤了一個更復雜的資料組合,比如一個用戶資訊的地址簿應用,任何單個欄位(如名字或生日)可能與另一個條目相同,但每個資料組合是唯一的,在這種情況下, ObjectKey 最合適,

UniqueKey

如果集合中有多個具有相同值的 Widget,或者如果您想確保每個 Widget 與其他 Widget 不同,則可以使用 UniqueKey, 在我們的例子中就使用了 UniqueKey,因為我們沒有將任何其他常量資料存盤在我們的色塊上,并且在構建 Widget 之前我們不知道顏色是什么,

不要在 Key 中使用亂數,如果你那樣設定,那么當每次構建 Widget 時,都會生成一個新的亂數,Element 樹將不會和 Widget 樹做一致的更新,

GlobalKeys

Global Keys有兩種用途,

它們允許 Widget 在應用中的任何位置更改父級而不會丟失 State ,或者可以使用它們在 Widget 樹 的完全不同的部分中訪問有關另一個 Widget 的資訊,

比如: 要在兩個不同的螢屏上顯示相同的 Widget,同時保持相同的 State,則需要使用 GlobalKeys,

在第二種情況下,您可能希望驗證密碼,但不希望與樹中的其他 Widget 共享該狀態資訊,可以使用 GlobalKey 持有一個表單 Form 的 State,

總結

如何合理適當的使用 Key:

  1. When: 當您想要保留 Widget 樹的狀態時,請使用 Key,例如: 當修改相同型別的 Widget 集合(如串列中)時
  2. Where: 將 Key 設定在要指明唯一身份的 Widget 樹的頂部
  3. Which: 根據在該 Widget 中存盤的資料型別選擇使用的不同型別的Key

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/355363.html

標籤:其他

上一篇:Kotlin學習筆記之可見性修飾符

下一篇:【Flutter核心類分析】深入理解BuildOwner

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more