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
。這會返回 FlutterView
,與給定內容相關聯的 build
方法所建立的 Widget 將繪製到其中。FlutterView
提供與先前在已棄用的 SingletonFlutterView
類別(由上述已棄用的 window
屬性返回)上可用的相同功能。但是,某些平台特定的功能已移至 PlatformDispatcher
,可以透過 View.of
返回的 FlutterView
,使用 FlutterView.platformDispatcher
存取。使用 View.of
是從上述已棄用屬性遷移的首選方式。
如果沒有可用的 BuildContext
來查詢 FlutterView
,可以直接查詢 PlatformDispatcher
來存取平台特定的功能。它還會在 PlatformDispatcher.views
中維護所有可用 FlutterView
的列表,以存取視圖特定的功能。如果可能,應該透過綁定(例如 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
,可透過視圖上的 platformDispatcher
getter 存取。
遷移前的程式碼
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
也努力使測試 API 更清晰且更簡潔,方法是使用與相關 getter 同名的 setter,而不是使用帶有 TestValue
後綴的 setter。
遷移前的程式碼
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.of
FlutterView
PlatformDispatcher
TestPlatformDispatcher
TestFlutterView
TestWidgetsFlutterBinding.window
相關問題
相關 PR
除非另有說明,否則本網站上的文件反映了 Flutter 的最新穩定版本。頁面上次更新於 2024-04-04。 檢視原始碼 或 回報問題。