window 單例已棄用
概述
#為支援多檢視和多視窗,window 單例已被棄用。之前依賴 window 單例的程式碼需要透過 View.of API 來查詢要操作的特定檢視,或者直接與 PlatformDispatcher 互動。
背景
#最初,Flutter 假設一個應用程式只有一個檢視(即 window),內容可以繪製到其中。在多檢視的世界中,這種假設不再適用,並且編碼了這種假設的 API 已被棄用。相反,依賴這些 API 的應用程式和庫必須選擇一個要操作的特定檢視,並按照本遷移指南中概述的步驟遷移到新的多檢視相容 API。
變更說明
#作為此更改一部分而被棄用的 API 包括:
dart:ui暴露的全域性window屬性。BaseBinding類上的window屬性,通常可以透過以下方式訪問:GestureBinding.instance.window,SchedulerBinding.instance.window,ServicesBinding.instance.window,PaintingBinding.instance.window,SemanticsBinding.instance.window,RendererBinding.instance.window,WidgetsBinding.instance.window,或WidgetTester.binding.window.
dart:ui中的SingletonFlutterView類。flutter_test中的TestWindow,其建構函式以及所有屬性和方法。
對於依賴這些已棄用 API 的應用程式和庫程式碼,存在以下遷移選項:
如果 BuildContext 可用,請考慮透過 View.of 查詢當前的 FlutterView。這將返回與給定上下文關聯的 build 方法所構建的小部件將被繪製到的 FlutterView。FlutterView 提供了與先前在已棄用的 window 屬性(如上所述)上可用的相同功能。但是,一些平臺特定的功能已移至 PlatformDispatcher,可以透過 View.of 返回的 FlutterView 上的 FlutterView.platformDispatcher 訪問。使用 View.of 是遷移離開上述已棄用屬性的首選方法。
如果沒有 BuildContext 可用於查詢 FlutterView,則可以直接諮詢 PlatformDispatcher 來訪問平臺特定的功能。它還維護著所有可用的 FlutterView 列表,儲存在 PlatformDispatcher.views 中,以便訪問檢視特定的功能。如果可能,應透過繫結(例如 WidgetsBinding.instance.platformDispatcher)來訪問 PlatformDispatcher,而不是使用靜態的 PlatformDispatcher.instance 屬性。這確保了 PlatformDispatcher 的功能可以在測試中被正確地模擬。
測試
#對於訪問 WidgetTester.binding.window 屬性以更改視窗屬性進行測試的程式碼,提供了以下遷移方案:
在 testWidgets 編寫的測試中,已新增兩個新屬性,它們共同取代了 TestWindow 的功能。
WidgetTester.view將提供一個TestFlutterView,其修改方式與WidgetTester.binding.window類似,但僅包含檢視特定的屬性,例如檢視的大小、其顯示畫素比等。WidgetTester.viewOf可用於某些多檢視用例,但對於從WidgetTester.binding.window的任何遷移都不應需要。
WidgetTester.platformDispatcher將提供對TestPlatformDispatcher的訪問,可用於修改平臺特定的屬性,例如平臺的區域設定、某些系統功能是否可用等。
遷移指南
#應用程式和庫程式碼在有 BuildContext 的情況下,應使用 View.of 來查詢與該上下文關聯的 FlutterView,而不是訪問靜態的 window 屬性。某些屬性已移至可以透過檢視的 platformDispatcher getter 訪問的 PlatformDispatcher。
遷移前的程式碼
Widget build(BuildContext context) {
final double dpr = WidgetsBinding.instance.window.devicePixelRatio;
final Locale locale = WidgetsBinding.instance.window.locale;
return Text('The device pixel ratio is $dpr and the locale is $locale.');
}遷移後的程式碼
Widget build(BuildContext context) {
final double dpr = View.of(context).devicePixelRatio;
final Locale locale = View.of(context).platformDispatcher.locale;
return Text('The device pixel ratio is $dpr and the locale is $locale.');
}如果沒有 BuildContext,則可以直接諮詢繫結公開的 PlatformDispatcher。
遷移前的程式碼
double getTextScaleFactor() {
return WidgetsBinding.instance.window.textScaleFactor;
}遷移後的程式碼
double getTextScaleFactor() {
// View.of(context).platformDispatcher.textScaleFactor if a BuildContext is available, otherwise:
return WidgetsBinding.instance.platformDispatcher.textScaleFactor;
}測試
#在 testWidget 編寫的測試中,應改用新的 view 和 platformDispatcher 訪問器。
設定檢視特定的屬性
#TestFlutterView 也致力於透過使用與相關 getter 同名的 setter,而不是帶有 TestValue 字尾的 setter,來使測試 API 更清晰、更簡潔。
遷移前的程式碼
testWidget('test name', (WidgetTester tester) async {
tester.binding.window.devicePixelRatioTestValue = 2.0;
tester.binding.window.displayFeaturesTestValue = <DisplayFeatures>[];
tester.binding.window.gestureSettingsTestValue = const GestureSettings(physicalTouchSlop: 100);
tester.binding.window.paddingTestValue = FakeViewPadding.zero;
tester.binding.window.physicalGeometryTestValue = const Rect.fromLTRB(0,0, 500, 800);
tester.binding.window.physicalSizeTestValue = const Size(300, 400);
tester.binding.window.systemGestureInsetsTestValue = FakeViewPadding.zero;
tester.binding.window.viewInsetsTestValue = FakeViewPadding.zero;
tester.binding.window.viewPaddingTestValue = FakeViewPadding.zero;
});遷移後的程式碼
testWidget('test name', (WidgetTester tester) async {
tester.view.devicePixelRatio = 2.0;
tester.view.displayFeatures = <DisplayFeatures>[];
tester.view.gestureSettings = const GestureSettings(physicalTouchSlop: 100);
tester.view.padding = FakeViewPadding.zero;
tester.view.physicalGeometry = const Rect.fromLTRB(0,0, 500, 800);
tester.view.physicalSize = const Size(300, 400);
tester.view.systemGestureInsets = FakeViewPadding.zero;
tester.view.viewInsets = FakeViewPadding.zero;
tester.view.viewPadding = FakeViewPadding.zero;
});重置檢視特定的屬性
#TestFlutterView 保留了重置單個屬性或整個檢視的功能,但為了更清晰和一致,這些方法的命名已從 clear<property>TestValue 和 clearAllTestValues 更改為 reset<property> 和 reset。
重置單個屬性
#遷移前的程式碼
testWidget('test name', (WidgetTester tester) async {
addTearDown(tester.binding.window.clearDevicePixelRatioTestValue);
addTearDown(tester.binding.window.clearDisplayFeaturesTestValue);
addTearDown(tester.binding.window.clearGestureSettingsTestValue);
addTearDown(tester.binding.window.clearPaddingTestValue);
addTearDown(tester.binding.window.clearPhysicalGeometryTestValue);
addTearDown(tester.binding.window.clearPhysicalSizeTestValue);
addTearDown(tester.binding.window.clearSystemGestureInsetsTestValue);
addTearDown(tester.binding.window.clearViewInsetsTestValue);
addTearDown(tester.binding.window.clearViewPaddingTestValue);
});遷移後的程式碼
testWidget('test name', (WidgetTester tester) async {
addTearDown(tester.view.resetDevicePixelRatio);
addTearDown(tester.view.resetDisplayFeatures);
addTearDown(tester.view.resetGestureSettings);
addTearDown(tester.view.resetPadding);
addTearDown(tester.view.resetPhysicalGeometry);
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetSystemGestureInsets);
addTearDown(tester.view.resetViewInsets);
addTearDown(tester.view.resetViewPadding);
});一次性重置所有屬性
#遷移前的程式碼
testWidget('test name', (WidgetTester tester) async {
addTearDown(tester.binding.window.clearAllTestValues);
});遷移後的程式碼
testWidget('test name', (WidgetTester tester) async {
addTearDown(tester.view.reset);
});設定平臺特定的屬性
#TestPlatformDispatcher 保留了與 TestWindow 相同的測試 setter 功能和命名約定,因此平臺特定屬性的遷移主要包括在新的 WidgetTester.platformDispatcher 訪問器上呼叫相同的 setter。
遷移前的程式碼
testWidgets('test name', (WidgetTester tester) async {
tester.binding.window.accessibilityFeaturesTestValue = FakeAccessibilityFeatures.allOn;
tester.binding.window.alwaysUse24HourFormatTestValue = false;
tester.binding.window.brieflyShowPasswordTestValue = true;
tester.binding.window.defaultRouteNameTestValue = '/test';
tester.binding.window.initialLifecycleStateTestValue = 'painting';
tester.binding.window.localesTestValue = <Locale>[const Locale('en-us'), const Locale('ar-jo')];
tester.binding.window.localeTestValue = const Locale('ar-jo');
tester.binding.window.nativeSpellCheckServiceDefinedTestValue = false;
tester.binding.window.platformBrightnessTestValue = Brightness.dark;
tester.binding.window.semanticsEnabledTestValue = true;
tester.binding.window.textScaleFactorTestValue = 2.0;
});遷移後的程式碼
testWidgets('test name', (WidgetTester tester) async {
tester.platformDispatcher.accessibilityFeaturesTestValue = FakeAccessibilityFeatures.allOn;
tester.platformDispatcher.alwaysUse24HourFormatTestValue = false;
tester.platformDispatcher.brieflyShowPasswordTestValue = true;
tester.platformDispatcher.defaultRouteNameTestValue = '/test';
tester.platformDispatcher.initialLifecycleStateTestValue = 'painting';
tester.platformDispatcher.localesTestValue = <Locale>[const Locale('en-us'), const Locale('ar-jo')];
tester.platformDispatcher.localeTestValue = const Locale('ar-jo');
tester.platformDispatcher.nativeSpellCheckServiceDefinedTestValue = false;
tester.platformDispatcher.platformBrightnessTestValue = Brightness.dark;
tester.platformDispatcher.semanticsEnabledTestValue = true;
tester.platformDispatcher.textScaleFactorTestValue = 2.0;
});重置平臺特定的屬性
#與設定屬性類似,重置平臺特定的屬性主要包括將訪問器從 binding.window 更改為 platformDispatcher 訪問器。
重置單個屬性
#遷移前的程式碼
testWidgets('test name', (WidgetTester tester) async {
addTeardown(tester.binding.window.clearAccessibilityFeaturesTestValue);
addTeardown(tester.binding.window.clearAlwaysUse24HourFormatTestValue);
addTeardown(tester.binding.window.clearBrieflyShowPasswordTestValue);
addTeardown(tester.binding.window.clearDefaultRouteNameTestValue);
addTeardown(tester.binding.window.clearInitialLifecycleStateTestValue);
addTeardown(tester.binding.window.clearLocalesTestValue);
addTeardown(tester.binding.window.clearLocaleTestValue);
addTeardown(tester.binding.window.clearNativeSpellCheckServiceDefinedTestValue);
addTeardown(tester.binding.window.clearPlatformBrightnessTestValue);
addTeardown(tester.binding.window.clearSemanticsEnabledTestValue);
addTeardown(tester.binding.window.clearTextScaleFactorTestValue);
});遷移後的程式碼
testWidgets('test name', (WidgetTester tester) async {
addTeardown(tester.platformDispatcher.clearAccessibilityFeaturesTestValue);
addTeardown(tester.platformDispatcher.clearAlwaysUse24HourFormatTestValue);
addTeardown(tester.platformDispatcher.clearBrieflyShowPasswordTestValue);
addTeardown(tester.platformDispatcher.clearDefaultRouteNameTestValue);
addTeardown(tester.platformDispatcher.clearInitialLifecycleStateTestValue);
addTeardown(tester.platformDispatcher.clearLocalesTestValue);
addTeardown(tester.platformDispatcher.clearLocaleTestValue);
addTeardown(tester.platformDispatcher.clearNativeSpellCheckServiceDefinedTestValue);
addTeardown(tester.platformDispatcher.clearPlatformBrightnessTestValue);
addTeardown(tester.platformDispatcher.clearSemanticsEnabledTestValue);
addTeardown(tester.platformDispatcher.clearTextScaleFactorTestValue);
});一次性重置所有屬性
#遷移前的程式碼
testWidgets('test name', (WidgetTester tester) async {
addTeardown(tester.binding.window.clearAllTestValues);
});遷移後的程式碼
testWidgets('test name', (WidgetTester tester) async {
addTeardown(tester.platformDispatcher.clearAllTestValues);
});時間線
#已登入版本:3.9.0-13.0.pre.20
穩定版本:3.10.0
參考資料
#API 文件
View.ofFlutterViewPlatformDispatcherTestPlatformDispatcherTestFlutterViewTestWidgetsFlutterBinding.window
相關問題
相關 PR