The window singleton is deprecated
Summary
#In preparation for supporting multiple views and multiple windows, the window
singleton has been deprecated. Code previously relying on the window
singleton
needs to look up the specific view it wants to operate on via the View.of
API
or interact with the PlatformDispatcher
directly.
Context
#Originally, Flutter assumed that an application would only consist of a single
view (the window
) into which content can be drawn. In a multi-view world, this
assumption no longer makes sense and the APIs encoding this assumption have
been deprecated. Instead, applications and libraries that relied on these APIs
must choose a specific view they want to operate on and
migrate to new multi-view compatible APIs as outlined in this migration guide.
Description of change
#The APIs that have been deprecated as part of this change are:
- The global
window
property exposed bydart:ui
. - The
window
property on theBaseBinding
class, which is usually accessed viaGestureBinding.instance.window
,SchedulerBinding.instance.window
,ServicesBinding.instance.window
,PaintingBinding.instance.window
,SemanticsBinding.instance.window
,RendererBinding.instance.window
,WidgetsBinding.instance.window
, orWidgetTester.binding.window
.
- The
SingletonFlutterView
class fromdart:ui
. TestWindow
fromflutter_test
, its constructors, and all of its properties and methods.
The following options exist to migrate application and library code that relies on these deprecated APIs:
If a BuildContext
is available, consider looking up the current FlutterView
via View.of
. This returns the FlutterView
into
which the widgets built by the build
method associated with the given context
will be drawn. The FlutterView
provides access to the same functionality
that was previously available on the deprecated SingletonFlutterView
class
returned by the deprecated window
properties mentioned above. However, some
of the platform-specific functionality has moved to the PlatformDispatcher
,
which can be accessed from the FlutterView
returned by View.of
via
FlutterView.platformDispatcher
. Using View.of
is the preferred way of
migrating away from the deprecated properties mentioned above.
If no BuildContext
is available to look up a FlutterView
, the
PlatformDispatcher
can be consulted directly to access platform-specific
functionality. It also maintains a list of all available FlutterView
s in
PlatformDispatcher.views
to access view-specific functionality. If possible,
the PlatformDispatcher
should be accessed via a binding (for example
WidgetsBinding.instance.platformDispatcher
) instead of using the static
PlatformDispatcher.instance
property. This ensures that the functionality
of the PlatformDispatcher
can be properly mocked out in tests.
Testing
#For tests that accessed the WidgetTester.binding.window
property to change
window properties for testing, the following migrations are available:
In tests written with testWidgets
, two new properties have been added that
together replace the functionality of TestWindow
.
WidgetTester.view
will provide aTestFlutterView
that can be modified similarly toWidgetTester.binding.window
, but with only view-specific properties such as the size of a view, its display pixel ratio, etc.WidgetTester.viewOf
is available for certain multi-view use cases, but should not be required for any migrations fromWidgetTester.binding.window
.
WidgetTester.platformDispatcher
will provide access to aTestPlatformDispatcher
that can be used to modify platform specific properties such as the platform's locale, whether certain system features are available, etc.
Migration guide
#Instead of accessing the static window
property, application and library code
that has access to a BuildContext
should use View.of
to look up the
FlutterView
the context is associated with. Some properties have moved to
the PlatformDispatcher
accessible from the view via the platformDispatcher
getter.
Code before migration:
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.');
}
Code after migration:
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.');
}
If no BuildContext
is available, the PlatformDispatcher
exposed by the
bindings can be consulted directly.
Code before migration:
double getTextScaleFactor() {
return WidgetsBinding.instance.window.textScaleFactor;
}
Code after migration:
double getTextScaleFactor() {
// View.of(context).platformDispatcher.textScaleFactor if a BuildContext is available, otherwise:
return WidgetsBinding.instance.platformDispatcher.textScaleFactor;
}
Testing
#In tests written with testWidget
, the new view
and platformDispatcher
accessors should be used instead.
Setting view-specific properties
#TestFlutterView
has also made an effort to make the test API clearer and more
concise by using setters with the same name as their related getter instead of
setters with the TestValue
suffix.
Code before migration:
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;
});
Code after migration
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;
});
Resetting view-specific properties
#TestFlutterView
retains the capability to reset individual properties or the
entire view but, in order to be more clear and consist, the naming of these
methods has changed from clear<property>TestValue
and clearAllTestValues
to
reset<property>
and reset
respectively.
Resetting individual properties
#Code before migration:
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);
});
Code after migration
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);
});
Resetting all properties at once
#Code before migration:
testWidget('test name', (WidgetTester tester) async {
addTearDown(tester.binding.window.clearAllTestValues);
});
Code after migration
testWidget('test name', (WidgetTester tester) async {
addTearDown(tester.view.reset);
});
Setting platform-specific properties
#TestPlatformDispatcher
retains the same functionality and naming scheme for
test setters as did TestWindow
, so migration of platform-specific properties
mainly consists of calling the same setters on the new
WidgetTester.platformDispatcher
accessor.
Code before migration:
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;
});
Code after migration:
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;
});
Resetting platform-specific properties
#Similarly to setting properties, resetting platform-specific properties consists
mainly of changing from the binding.window
accessor to the
platformDispatcher
accessor.
Resetting individual properties
#Code before migration:
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);
});
Code after migration:
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);
});
Resetting all properties at once
#Code before migration:
testWidgets('test name', (WidgetTester tester) async {
addTeardown(tester.binding.window.clearAllTestValues);
});
Code after migration:
testWidgets('test name', (WidgetTester tester) async {
addTeardown(tester.platformDispatcher.clearAllTestValues);
});
Timeline
#Landed in version: 3.9.0-13.0.pre.20
In stable release: 3.10.0
References
#API documentation:
View.of
FlutterView
PlatformDispatcher
TestPlatformDispatcher
TestFlutterView
TestWidgetsFlutterBinding.window
Relevant issues:
Relevant PRs:
- Deprecate SingletonFlutterWindow and global window singleton
- Deprecate BindingBase.window
- Deprecates
TestWindow
除非另有说明,本文档之所提及适用于 Flutter 的最新稳定版本,本页面最后更新时间: 2024-04-04。 查看文档源码 或者 为本页面内容提出建议。