「區域不匹配」訊息
摘要
#從 Flutter 3.10 開始,當使用 Zones 時,框架會偵測到不匹配的情況,並在偵錯建置中將其報告到主控台。
背景
#Zones 是一種在 Dart 中管理回呼的機制。雖然主要用於覆寫測試中的 print
和 Timer
邏輯,以及在測試中捕獲錯誤,但有時也會用於將全域變數的作用域限定於應用程式的某些部分。
Flutter 要求(而且一直以來都要求)所有框架程式碼都在同一個區域中執行。值得注意的是,這表示對 WidgetsFlutterBinding.ensureInitialized()
的呼叫應與對 runApp()
的呼叫在同一個區域中執行。
過去,Flutter 並未偵測到此類不匹配的情況。這有時會導致難以理解和難以偵錯的問題。例如,使用沒有存取其預期的 zoneValues
的區域可能會調用鍵盤輸入的回呼。根據我們的經驗,大多數(如果不是全部)以無法保證 Flutter 框架的所有部分都在同一個區域中工作的方式使用 Zones 的程式碼都存在一些潛在的錯誤。這些錯誤通常與 Zones 的使用無關。
為了幫助意外違反此不變式的開發人員,從 Flutter 3.10 開始,當偵測到不匹配時,會在偵錯建置中印出一個非致命的警告。該警告看起來如下:
════════ Exception caught by Flutter framework ════════════════════════════════════
The following assertion was thrown during runApp:
Zone mismatch.
The Flutter bindings were initialized in a different zone than is now being used.
This will likely cause confusion and bugs as any zone-specific configuration will
inconsistently use the configuration of the original binding initialization zone or
this zone based on hard-to-predict factors such as which zone was active when a
particular callback was set.
It is important to use the same zone when calling `ensureInitialized` on the
binding as when calling `runApp` later.
To make this warning fatal, set BindingBase.debugZoneErrorsAreFatal to true before
the bindings are initialized (i.e. as the first statement in `void main() { }`).
[...]
═══════════════════════════════════════════════════════════════════════════════════
可以將 BindingBase.debugZoneErrorsAreFatal
設定為 true
,使警告變為致命錯誤。此旗標可能會在未來版本的 Flutter 中預設為 true
。
遷移指南
#消除此訊息的最佳方法是從應用程式中移除 Zones 的使用。Zones 可能很難偵錯,因為它們本質上是全域變數,並破壞了封裝。最佳實務是避免使用全域變數和 zones。
如果移除 zones 不是一個選項(例如,因為應用程式依賴於第三方函式庫,而該函式庫依賴於 zones 進行組態),則對 Flutter 框架的各種呼叫應移至都在同一個區域中。通常,這表示將對 WidgetsFlutterBinding.ensureInitialized()
的呼叫移至與對 runApp()
的呼叫相同的閉包中。
當執行 runApp
的區域使用從外掛程式獲得的 zoneValues
初始化時,可能會很麻煩(這需要先呼叫 WidgetsFlutterBinding.ensureInitialized()
)。
在這種情況下的一種選項是在 zoneValues
中放置一個可變物件,並在值可用時使用該值更新該物件。
import 'dart:async';
import 'package:flutter/material.dart';
class Mutable<T> {
Mutable(this.value);
T value;
}
void main() {
var myValue = Mutable<double>(0.0);
Zone.current.fork(
zoneValues: {
'myKey': myValue,
}
).run(() {
WidgetsFlutterBinding.ensureInitialized();
var newValue = ...; // obtain value from plugin
myValue.value = newValue; // update value in Zone
runApp(...);
});
}
在需要使用 myKey
的程式碼中,可以使用 Zone.current['myKey'].value
間接取得。
當此類解決方案不起作用,因為第三方相依性需要特定類型的特定 zoneValues
鍵時,可以將對相依性的所有呼叫包裝在提供適當值的 Zone
呼叫中。
強烈建議以這種方式使用 zones 的套件遷移到更易於維護的解決方案。
時間軸
#已於版本中加入:3.9.0-9.0.pre
穩定版本:3.10.0
參考資料
#API 文件
相關問題
- Issue 94123:當 ensureInitialized 在與 runApp 不同的區域中呼叫時,Flutter 框架不會發出警告
相關的 PR
- PR 122836:斷言 runApp 與 binding.ensureInitialized 在同一個區域中呼叫
除非另有說明,否則本網站上的文件反映了 Flutter 的最新穩定版本。頁面最後更新於 2024-04-04。 檢視原始碼 或 回報問題。