亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Flutter開發(fā)setState能否在build中直接調(diào)用詳解

 更新時(shí)間:2022年10月13日 11:26:55   作者:deepfunc  
這篇文章主要為大家介紹了Flutter開發(fā)setState能否在build中直接調(diào)用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

兩種情況

setState() 能在 build() 中直接調(diào)用嗎?答案是能也不能。

來看一段簡單的代碼:

import 'package:flutter/material.dart';
class TestPage extends StatefulWidget {
  const TestPage({super.key});
  @override
  State<TestPage> createState() => _State();
}
class _State extends State<TestPage> {
  int _count = 0;
  @override
  Widget build(BuildContext context) {
    setState(() {
      _count++;
    });
    return Scaffold(
      appBar: AppBar(
        title: const Text('測試頁面'),
      ),
      body: Center(
        child: Text(
          '$_count',
          style: const TextStyle(fontSize: 24),
        ),
      ),
    );
  }
}

跑起來后代碼不會(huì)報(bào)錯(cuò),Text('$_count') 顯示結(jié)果是 1,看來 build() 調(diào)用 setState() 沒啥問題呀。小改一下,來看看這個(gè):

class _State extends State&lt;TestPage&gt; {
  int _count = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('測試頁面'),
      ),
      body: Center(
        child: Builder(
          builder: (context) {
            setState(() {
              _count++;
            });
            return Text(
              '$_count',
              style: const TextStyle(fontSize: 24),
            );
          }
        ),
      ),
    );
  }
}

改動(dòng)主要是在 Text 上面加了一個(gè) Builder,然后把 setState() 放在了 Builder 的 builder 中去調(diào)用。運(yùn)行起來,結(jié)果出現(xiàn)報(bào)錯(cuò)了:The following assertion was thrown building Builder(dirty): setState() or markNeedsBuild() called during build.提示在 Builder 的 build() 過程中出現(xiàn)了斷言錯(cuò)誤:build() 中不能調(diào)用 setState() 或 markNeedsBuild()。

這是什么情況呢,為什么第一種情況下可以在 build() 中調(diào)用 setState() 而第二種情況不行?下面來簡單地分析下其中包含的原理。

原理分析

先說一下結(jié)論,在 build() 中直接調(diào)用 setState() 要滿足一個(gè)前提條件:

如果當(dāng)前有組件 A 處于 build() 中,那么 setState() 引起 rebuild 的組件必須是 A 或者 A 的子孫組件,不能是 A 的祖先組件。

這是因?yàn)榻M件 build 的順序是從父到子,如果在子組件 build 的過程中執(zhí)行 setState() 之類會(huì)引起父組件的重新 build 那就死循環(huán)肯定是不行的。

接下來看下 Flutter 源碼中是如何判斷和控制的。setState() 的內(nèi)部會(huì)調(diào)用 _element!.markNeedsBuild()markNeedsBuild() 中有如下代碼:

void markNeedsBuild() {
  // ...
  // 前半部分,斷言重新 build 是否滿足上面說的前提。
  assert(() {
    if (owner!._debugBuilding) {
      assert(owner!._debugCurrentBuildTarget != null);
      assert(owner!._debugStateLocked);
      // _debugIsInScope() 用來判斷是否滿足前提條件。
      if (_debugIsInScope(owner!._debugCurrentBuildTarget!)) {
        return true;
      }
      if (!_debugAllowIgnoredCallsToMarkNeedsBuild) {
        final List&lt;DiagnosticsNode&gt; information = &lt;DiagnosticsNode&gt;[
          ErrorSummary('setState() or markNeedsBuild() called during build.'),
          // ...
        ];
        // ...
      }
      // ...
    }());
  // ...
}

markNeedsBuild() 代碼的前半部分有斷言來處理是否滿足上面說到的前提條件,_debugCurrentBuildTarget 就是當(dāng)前正處于 build 狀態(tài)的 element。_debugCurrentBuildTarget() 的內(nèi)容如下:

bool _debugIsInScope(Element target) {
  Element? current = this;
  while (current != null) {
    if (target == current) {
      return true;
    }
    current = current._parent;
  }
  return false;
}

_debugIsInScope() 中的 this 就是調(diào)用 setState() 會(huì)引起 rebuild 的組件,target 就是當(dāng)前正處于 build 的組件。其中的 while 循環(huán)會(huì)逐步比對 current 及其父組件是否當(dāng)前 build 的對象,找到了才會(huì)返回 true,否則就是 false。如果是 false,則后面的斷言就會(huì)出現(xiàn)錯(cuò)誤:setState() or markNeedsBuild() called during build.

如果當(dāng)前有組件正在 build 那么決不能引起父組件的 rebuild,我們來看下前面舉例報(bào)錯(cuò)的第二種情況。Builder 是 TestPage 的子組件,Builder 的 builder 方法里調(diào)用的 setState 是 TestPage 上的,也就是在子組件的 build 過程中使父組件 rebuild 了,那么就會(huì)引起斷言失??;而第一種情況下是在 TestPage 的 build 過程中調(diào)用 setState 使自己重新 rebuild,可以滿足結(jié)論的前提,所以是可以調(diào)用的。

這里我們可以接著想下在第一種情況下,組件自己的 build 過程中調(diào)用了 setState 引起了自己重新 rebuild 的時(shí)候不是也會(huì)死循環(huán)了嗎?我們接著看下 markNeedsBuild() 的后半部分代碼,如果斷言成功后后面的邏輯:

void markNeedsBuild() {
  // ...
  // 前半部分是斷言。
  if (dirty) {
    return;
  }
  _dirty = true;
  owner!.scheduleBuildFor(this);
}

這里可以看到組件在 build 過程中 markNeedsBuild() 會(huì)使組件變?yōu)?dirty 狀態(tài),這個(gè)時(shí)候在 build 中直接調(diào)用 setState 后發(fā)現(xiàn)已經(jīng)是 dirty 狀態(tài)后會(huì)直接返回,而不會(huì)調(diào)度重新 build,所以就沒有問題了。

總結(jié)

通過以上的分析我們知道了 Flutter 是如何判斷如果在 build 過程中直接調(diào)用 setState 是否合法的。當(dāng)然我們在寫代碼的時(shí)候是不會(huì)在 build() 中直接調(diào)用 setState 的,了解以上過程更有助于我們排查問題和學(xué)習(xí) Flutter 的運(yùn)行原理。

以上就是Flutter開發(fā)setState能否在build中直接調(diào)用詳解的詳細(xì)內(nèi)容,更多關(guān)于Flutter setState調(diào)用build的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論