Unverified Commit 92125ed3 authored by Matt Carroll's avatar Matt Carroll Committed by GitHub

Enable dependency injection of Window instead of using static property (#27389)

parent 3ba97da9
...@@ -32,8 +32,14 @@ Future<void> main() async { ...@@ -32,8 +32,14 @@ Future<void> main() async {
await tester.pump(); // Start drawer animation await tester.pump(); // Start drawer animation
await tester.pump(const Duration(seconds: 1)); // Complete drawer animation await tester.pump(const Duration(seconds: 1)); // Complete drawer animation
final TestViewConfiguration big = TestViewConfiguration(size: const Size(360.0, 640.0)); final TestViewConfiguration big = TestViewConfiguration(
final TestViewConfiguration small = TestViewConfiguration(size: const Size(355.0, 635.0)); size: const Size(360.0, 640.0),
window: RendererBinding.instance.window,
);
final TestViewConfiguration small = TestViewConfiguration(
size: const Size(355.0, 635.0),
window: RendererBinding.instance.window,
);
final RenderView renderView = WidgetsBinding.instance.renderView; final RenderView renderView = WidgetsBinding.instance.renderView;
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.benchmark; binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.benchmark;
......
...@@ -6,7 +6,8 @@ import 'dart:async'; ...@@ -6,7 +6,8 @@ import 'dart:async';
import 'dart:convert' show json; import 'dart:convert' show json;
import 'dart:developer' as developer; import 'dart:developer' as developer;
import 'dart:io' show exit; import 'dart:io' show exit;
import 'dart:ui' show saveCompilationTrace; // Before adding any more dart:ui imports, pleaes read the README.
import 'dart:ui' as ui show saveCompilationTrace, Window, window;
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
...@@ -66,6 +67,24 @@ abstract class BindingBase { ...@@ -66,6 +67,24 @@ abstract class BindingBase {
static bool _debugInitialized = false; static bool _debugInitialized = false;
static bool _debugServiceExtensionsRegistered = false; static bool _debugServiceExtensionsRegistered = false;
/// The window to which this binding is bound.
///
/// A number of additional bindings are defined as extensions of [BindingBase],
/// e.g., [ServicesBinding], [RendererBinding], and [WidgetsBinding]. Each of
/// these bindings define behaviors that interact with a [ui.Window], e.g.,
/// [ServicesBinding] registers a [ui.Window.onPlatformMessage] handler, and
/// [RendererBinding] registers [ui.Window.onMetricsChanged],
/// [ui.Window.onTextScaleFactorChanged], [ui.Window.onSemanticsEnabledChanged],
/// and [ui.Window.onSemanticsAction] handlers.
///
/// Each of these other bindings could individually access a [Window] statically,
/// but that would preclude the ability to test these behaviors with a fake
/// window for verification purposes. Therefore, [BindingBase] exposes this
/// [Window] for use by other bindings. A subclass of [BindingBase], such as
/// [TestWidgetsFlutterBinding], can override this accessor to return a
/// different [Window] implementation, such as a [TestWindow].
ui.Window get window => ui.window;
/// The initialization method. Subclasses override this method to hook into /// The initialization method. Subclasses override this method to hook into
/// the platform and otherwise configure their services. Subclasses must call /// the platform and otherwise configure their services. Subclasses must call
/// "super.initInstances()". /// "super.initInstances()".
...@@ -122,7 +141,7 @@ abstract class BindingBase { ...@@ -122,7 +141,7 @@ abstract class BindingBase {
name: 'saveCompilationTrace', name: 'saveCompilationTrace',
callback: (Map<String, String> parameters) async { callback: (Map<String, String> parameters) async {
return <String, dynamic> { return <String, dynamic> {
'value': saveCompilationTrace(), 'value': ui.saveCompilationTrace(),
}; };
} }
); );
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:collection'; import 'dart:collection';
import 'dart:ui' as ui show window, PointerDataPacket; import 'dart:ui' as ui show PointerDataPacket;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
...@@ -62,7 +62,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H ...@@ -62,7 +62,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
void initInstances() { void initInstances() {
super.initInstances(); super.initInstances();
_instance = this; _instance = this;
ui.window.onPointerDataPacket = _handlePointerDataPacket; window.onPointerDataPacket = _handlePointerDataPacket;
} }
@override @override
...@@ -80,7 +80,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H ...@@ -80,7 +80,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
void _handlePointerDataPacket(ui.PointerDataPacket packet) { void _handlePointerDataPacket(ui.PointerDataPacket packet) {
// We convert pointer data to logical pixels so that e.g. the touch slop can be // We convert pointer data to logical pixels so that e.g. the touch slop can be
// defined in a device-independent manner. // defined in a device-independent manner.
_pendingPointerEvents.addAll(PointerEventConverter.expand(packet.data, ui.window.devicePixelRatio)); _pendingPointerEvents.addAll(PointerEventConverter.expand(packet.data, window.devicePixelRatio));
if (!locked) if (!locked)
_flushPointerEventQueue(); _flushPointerEventQueue();
} }
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:developer'; import 'dart:developer';
import 'dart:typed_data'; import 'dart:typed_data';
import 'dart:ui' as ui show window;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
...@@ -31,7 +30,7 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture ...@@ -31,7 +30,7 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
onSemanticsOwnerCreated: _handleSemanticsOwnerCreated, onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed, onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
); );
ui.window window
..onMetricsChanged = handleMetricsChanged ..onMetricsChanged = handleMetricsChanged
..onTextScaleFactorChanged = handleTextScaleFactorChanged ..onTextScaleFactorChanged = handleTextScaleFactorChanged
..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
...@@ -131,7 +130,7 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture ...@@ -131,7 +130,7 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
/// Called automatically when the binding is created. /// Called automatically when the binding is created.
void initRenderView() { void initRenderView() {
assert(renderView == null); assert(renderView == null);
renderView = RenderView(configuration: createViewConfiguration()); renderView = RenderView(configuration: createViewConfiguration(), window: window);
renderView.scheduleInitialFrame(); renderView.scheduleInitialFrame();
} }
...@@ -181,9 +180,9 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture ...@@ -181,9 +180,9 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
/// this to force the display into 800x600 when a test is run on the device /// this to force the display into 800x600 when a test is run on the device
/// using `flutter run`. /// using `flutter run`.
ViewConfiguration createViewConfiguration() { ViewConfiguration createViewConfiguration() {
final double devicePixelRatio = ui.window.devicePixelRatio; final double devicePixelRatio = window.devicePixelRatio;
return ViewConfiguration( return ViewConfiguration(
size: ui.window.physicalSize / devicePixelRatio, size: window.physicalSize / devicePixelRatio,
devicePixelRatio: devicePixelRatio, devicePixelRatio: devicePixelRatio,
); );
} }
...@@ -198,12 +197,12 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture ...@@ -198,12 +197,12 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
// the logical coordinates of the event location back to device pixels // the logical coordinates of the event location back to device pixels
// here. // here.
return renderView.layer return renderView.layer
.find<MouseTrackerAnnotation>(offset * ui.window.devicePixelRatio); .find<MouseTrackerAnnotation>(offset * window.devicePixelRatio);
}); });
} }
void _handleSemanticsEnabledChanged() { void _handleSemanticsEnabledChanged() {
setSemanticsEnabled(ui.window.semanticsEnabled); setSemanticsEnabled(window.semanticsEnabled);
} }
/// Whether the render tree associated with this binding should produce a tree /// Whether the render tree associated with this binding should produce a tree
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
import 'dart:developer'; import 'dart:developer';
import 'dart:io' show Platform; import 'dart:io' show Platform;
import 'dart:ui' as ui show Scene, SceneBuilder, window; import 'dart:ui' as ui show Scene, SceneBuilder, Window;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
...@@ -56,8 +56,10 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> ...@@ -56,8 +56,10 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
RenderView({ RenderView({
RenderBox child, RenderBox child,
@required ViewConfiguration configuration, @required ViewConfiguration configuration,
@required ui.Window window,
}) : assert(configuration != null), }) : assert(configuration != null),
_configuration = configuration { _configuration = configuration,
_window = window {
this.child = child; this.child = child;
} }
...@@ -82,6 +84,8 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> ...@@ -82,6 +84,8 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
markNeedsLayout(); markNeedsLayout();
} }
ui.Window _window;
/// Whether Flutter should automatically compute the desired system UI. /// Whether Flutter should automatically compute the desired system UI.
/// ///
/// When this setting is enabled, Flutter will hit-test the layer tree at the /// When this setting is enabled, Flutter will hit-test the layer tree at the
...@@ -195,7 +199,7 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> ...@@ -195,7 +199,7 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
final ui.Scene scene = layer.buildScene(builder); final ui.Scene scene = layer.buildScene(builder);
if (automaticSystemUiAdjustment) if (automaticSystemUiAdjustment)
_updateSystemChrome(); _updateSystemChrome();
ui.window.render(scene); _window.render(scene);
scene.dispose(); scene.dispose();
assert(() { assert(() {
if (debugRepaintRainbowEnabled || debugRepaintTextRainbowEnabled) if (debugRepaintRainbowEnabled || debugRepaintTextRainbowEnabled)
...@@ -209,8 +213,8 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> ...@@ -209,8 +213,8 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
void _updateSystemChrome() { void _updateSystemChrome() {
final Rect bounds = paintBounds; final Rect bounds = paintBounds;
final Offset top = Offset(bounds.center.dx, ui.window.padding.top / ui.window.devicePixelRatio); final Offset top = Offset(bounds.center.dx, _window.padding.top / _window.devicePixelRatio);
final Offset bottom = Offset(bounds.center.dx, bounds.center.dy - ui.window.padding.bottom / ui.window.devicePixelRatio); final Offset bottom = Offset(bounds.center.dx, bounds.center.dy - _window.padding.bottom / _window.devicePixelRatio);
final SystemUiOverlayStyle upperOverlayStyle = layer.find<SystemUiOverlayStyle>(top); final SystemUiOverlayStyle upperOverlayStyle = layer.find<SystemUiOverlayStyle>(top);
// Only android has a customizable system navigation bar. // Only android has a customizable system navigation bar.
SystemUiOverlayStyle lowerOverlayStyle; SystemUiOverlayStyle lowerOverlayStyle;
...@@ -254,10 +258,10 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> ...@@ -254,10 +258,10 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
properties.add(DiagnosticsNode.message('debug mode enabled - ${Platform.operatingSystem}')); properties.add(DiagnosticsNode.message('debug mode enabled - ${Platform.operatingSystem}'));
return true; return true;
}()); }());
properties.add(DiagnosticsProperty<Size>('window size', ui.window.physicalSize, tooltip: 'in physical pixels')); properties.add(DiagnosticsProperty<Size>('window size', _window.physicalSize, tooltip: 'in physical pixels'));
properties.add(DoubleProperty('device pixel ratio', ui.window.devicePixelRatio, tooltip: 'physical pixels per logical pixel')); properties.add(DoubleProperty('device pixel ratio', _window.devicePixelRatio, tooltip: 'physical pixels per logical pixel'));
properties.add(DiagnosticsProperty<ViewConfiguration>('configuration', configuration, tooltip: 'in logical pixels')); properties.add(DiagnosticsProperty<ViewConfiguration>('configuration', configuration, tooltip: 'in logical pixels'));
if (ui.window.semanticsEnabled) if (_window.semanticsEnabled)
properties.add(DiagnosticsNode.message('semantics enabled')); properties.add(DiagnosticsNode.message('semantics enabled'));
} }
} }
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:collection'; import 'dart:collection';
import 'dart:developer'; import 'dart:developer';
import 'dart:ui' as ui show window;
import 'dart:ui' show AppLifecycleState; import 'dart:ui' show AppLifecycleState;
import 'package:collection/collection.dart' show PriorityQueue, HeapPriorityQueue; import 'package:collection/collection.dart' show PriorityQueue, HeapPriorityQueue;
...@@ -191,8 +190,8 @@ mixin SchedulerBinding on BindingBase, ServicesBinding { ...@@ -191,8 +190,8 @@ mixin SchedulerBinding on BindingBase, ServicesBinding {
void initInstances() { void initInstances() {
super.initInstances(); super.initInstances();
_instance = this; _instance = this;
ui.window.onBeginFrame = _handleBeginFrame; window.onBeginFrame = _handleBeginFrame;
ui.window.onDrawFrame = _handleDrawFrame; window.onDrawFrame = _handleDrawFrame;
SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage); SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
} }
...@@ -682,7 +681,7 @@ mixin SchedulerBinding on BindingBase, ServicesBinding { ...@@ -682,7 +681,7 @@ mixin SchedulerBinding on BindingBase, ServicesBinding {
debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.'); debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
return true; return true;
}()); }());
ui.window.scheduleFrame(); window.scheduleFrame();
_hasScheduledFrame = true; _hasScheduledFrame = true;
} }
...@@ -713,7 +712,7 @@ mixin SchedulerBinding on BindingBase, ServicesBinding { ...@@ -713,7 +712,7 @@ mixin SchedulerBinding on BindingBase, ServicesBinding {
debugPrintStack(label: 'scheduleForcedFrame() called. Current phase is $schedulerPhase.'); debugPrintStack(label: 'scheduleForcedFrame() called. Current phase is $schedulerPhase.');
return true; return true;
}()); }());
ui.window.scheduleFrame(); window.scheduleFrame();
_hasScheduledFrame = true; _hasScheduledFrame = true;
} }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' as ui show AccessibilityFeatures, window; import 'dart:ui' as ui show AccessibilityFeatures;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
...@@ -21,7 +21,7 @@ mixin SemanticsBinding on BindingBase { ...@@ -21,7 +21,7 @@ mixin SemanticsBinding on BindingBase {
void initInstances() { void initInstances() {
super.initInstances(); super.initInstances();
_instance = this; _instance = this;
_accessibilityFeatures = ui.window.accessibilityFeatures; _accessibilityFeatures = window.accessibilityFeatures;
} }
/// Called when the platform accessibility features change. /// Called when the platform accessibility features change.
...@@ -29,7 +29,7 @@ mixin SemanticsBinding on BindingBase { ...@@ -29,7 +29,7 @@ mixin SemanticsBinding on BindingBase {
/// See [Window.onAccessibilityFeaturesChanged]. /// See [Window.onAccessibilityFeaturesChanged].
@protected @protected
void handleAccessibilityFeaturesChanged() { void handleAccessibilityFeaturesChanged() {
_accessibilityFeatures = ui.window.accessibilityFeatures; _accessibilityFeatures = window.accessibilityFeatures;
} }
/// The currently active set of [AccessibilityFeatures]. /// The currently active set of [AccessibilityFeatures].
......
...@@ -13,6 +13,7 @@ import 'package:flutter/painting.dart' show MatrixUtils, TransformProperty; ...@@ -13,6 +13,7 @@ import 'package:flutter/painting.dart' show MatrixUtils, TransformProperty;
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:vector_math/vector_math_64.dart'; import 'package:vector_math/vector_math_64.dart';
import 'binding.dart' show SemanticsBinding;
import 'semantics_event.dart'; import 'semantics_event.dart';
export 'dart:ui' show SemanticsAction; export 'dart:ui' show SemanticsAction;
...@@ -2473,7 +2474,7 @@ class SemanticsOwner extends ChangeNotifier { ...@@ -2473,7 +2474,7 @@ class SemanticsOwner extends ChangeNotifier {
final CustomSemanticsAction action = CustomSemanticsAction.getAction(actionId); final CustomSemanticsAction action = CustomSemanticsAction.getAction(actionId);
builder.updateCustomAction(id: actionId, label: action.label, hint: action.hint, overrideId: action.action?.index ?? -1); builder.updateCustomAction(id: actionId, label: action.label, hint: action.hint, overrideId: action.action?.index ?? -1);
} }
ui.window.updateSemantics(builder.build()); SemanticsBinding.instance.window.updateSemantics(builder.build());
notifyListeners(); notifyListeners();
} }
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; import 'dart:async';
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
...@@ -20,11 +19,16 @@ mixin ServicesBinding on BindingBase { ...@@ -20,11 +19,16 @@ mixin ServicesBinding on BindingBase {
@override @override
void initInstances() { void initInstances() {
super.initInstances(); super.initInstances();
ui.window _instance = this;
window
..onPlatformMessage = BinaryMessages.handlePlatformMessage; ..onPlatformMessage = BinaryMessages.handlePlatformMessage;
initLicenses(); initLicenses();
} }
/// The current [ServicesBinding], if one has been created.
static ServicesBinding get instance => _instance;
static ServicesBinding _instance;
/// Adds relevant licenses to the [LicenseRegistry]. /// Adds relevant licenses to the [LicenseRegistry].
/// ///
/// By default, the [ServicesBinding]'s implementation of [initLicenses] adds /// By default, the [ServicesBinding]'s implementation of [initLicenses] adds
......
...@@ -8,6 +8,7 @@ import 'dart:ui' as ui; ...@@ -8,6 +8,7 @@ import 'dart:ui' as ui;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'binding.dart' show ServicesBinding;
import 'platform_channel.dart'; import 'platform_channel.dart';
typedef _MessageHandler = Future<ByteData> Function(ByteData message); typedef _MessageHandler = Future<ByteData> Function(ByteData message);
...@@ -36,7 +37,7 @@ class BinaryMessages { ...@@ -36,7 +37,7 @@ class BinaryMessages {
static Future<ByteData> _sendPlatformMessage(String channel, ByteData message) { static Future<ByteData> _sendPlatformMessage(String channel, ByteData message) {
final Completer<ByteData> completer = Completer<ByteData>(); final Completer<ByteData> completer = Completer<ByteData>();
ui.window.sendPlatformMessage(channel, message, (ByteData reply) { ServicesBinding.instance.window.sendPlatformMessage(channel, message, (ByteData reply) {
try { try {
completer.complete(reply); completer.complete(reply);
} catch (exception, stack) { } catch (exception, stack) {
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:collection' show HashMap; import 'dart:collection' show HashMap;
import 'dart:ui' as ui show window;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
...@@ -710,7 +709,7 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv ...@@ -710,7 +709,7 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
void initState() { void initState() {
super.initState(); super.initState();
_updateNavigator(); _updateNavigator();
_locale = _resolveLocales(ui.window.locales, widget.supportedLocales); _locale = _resolveLocales(WidgetsBinding.instance.window.locales, widget.supportedLocales);
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
} }
...@@ -996,7 +995,7 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv ...@@ -996,7 +995,7 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
@override @override
void didChangeAccessibilityFeatures() { void didChangeAccessibilityFeatures() {
setState(() { setState(() {
// The properties of ui.window have changed. We use them in our build // The properties of window have changed. We use them in our build
// function, so we need setState(), but we don't cache anything locally. // function, so we need setState(), but we don't cache anything locally.
}); });
} }
...@@ -1007,7 +1006,7 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv ...@@ -1007,7 +1006,7 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
@override @override
void didChangeMetrics() { void didChangeMetrics() {
setState(() { setState(() {
// The properties of ui.window have changed. We use them in our build // The properties of window have changed. We use them in our build
// function, so we need setState(), but we don't cache anything locally. // function, so we need setState(), but we don't cache anything locally.
}); });
} }
...@@ -1015,8 +1014,8 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv ...@@ -1015,8 +1014,8 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
@override @override
void didChangeTextScaleFactor() { void didChangeTextScaleFactor() {
setState(() { setState(() {
// The textScaleFactor property of ui.window has changed. We reference // The textScaleFactor property of window has changed. We reference
// ui.window in our build function, so we need to call setState(), but // window in our build function, so we need to call setState(), but
// we don't need to cache anything locally. // we don't need to cache anything locally.
}); });
} }
...@@ -1077,12 +1076,12 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv ...@@ -1077,12 +1076,12 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
if (_navigator != null) { if (_navigator != null) {
navigator = Navigator( navigator = Navigator(
key: _navigator, key: _navigator,
// If ui.window.defaultRouteName isn't '/', we should assume it was set // If window.defaultRouteName isn't '/', we should assume it was set
// intentionally via `setInitialRoute`, and should override whatever // intentionally via `setInitialRoute`, and should override whatever
// is in [widget.initialRoute]. // is in [widget.initialRoute].
initialRoute: ui.window.defaultRouteName != Navigator.defaultRouteName initialRoute: WidgetsBinding.instance.window.defaultRouteName != Navigator.defaultRouteName
? ui.window.defaultRouteName ? WidgetsBinding.instance.window.defaultRouteName
: widget.initialRoute ?? ui.window.defaultRouteName, : widget.initialRoute ?? WidgetsBinding.instance.window.defaultRouteName,
onGenerateRoute: _onGenerateRoute, onGenerateRoute: _onGenerateRoute,
onUnknownRoute: _onUnknownRoute, onUnknownRoute: _onUnknownRoute,
observers: widget.navigatorObservers, observers: widget.navigatorObservers,
...@@ -1183,7 +1182,7 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv ...@@ -1183,7 +1182,7 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
assert(_debugCheckLocalizations(appLocale)); assert(_debugCheckLocalizations(appLocale));
return MediaQuery( return MediaQuery(
data: MediaQueryData.fromWindow(ui.window), data: MediaQueryData.fromWindow(WidgetsBinding.instance.window),
child: Localizations( child: Localizations(
locale: appLocale, locale: appLocale,
delegates: _localizationsDelegates.toList(), delegates: _localizationsDelegates.toList(),
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:developer' as developer; import 'dart:developer' as developer;
import 'dart:ui' show AppLifecycleState, Locale, AccessibilityFeatures; import 'dart:ui' show AppLifecycleState, Locale, AccessibilityFeatures;
import 'dart:ui' as ui show window;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
...@@ -141,7 +140,7 @@ abstract class WidgetsBindingObserver { ...@@ -141,7 +140,7 @@ abstract class WidgetsBindingObserver {
/// ///
/// @override /// @override
/// void didChangeMetrics() { /// void didChangeMetrics() {
/// setState(() { _lastSize = ui.window.physicalSize; }); /// setState(() { _lastSize = WidgetsBinding.instance.window.physicalSize; });
/// } /// }
/// ///
/// @override /// @override
...@@ -197,7 +196,7 @@ abstract class WidgetsBindingObserver { ...@@ -197,7 +196,7 @@ abstract class WidgetsBindingObserver {
/// ///
/// @override /// @override
/// void didChangeTextScaleFactor() { /// void didChangeTextScaleFactor() {
/// setState(() { _lastTextScaleFactor = ui.window.textScaleFactor; }); /// setState(() { _lastTextScaleFactor = WidgetsBinding.instance.window.textScaleFactor; });
/// } /// }
/// ///
/// @override /// @override
...@@ -250,8 +249,8 @@ mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererB ...@@ -250,8 +249,8 @@ mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererB
super.initInstances(); super.initInstances();
_instance = this; _instance = this;
buildOwner.onBuildScheduled = _handleBuildScheduled; buildOwner.onBuildScheduled = _handleBuildScheduled;
ui.window.onLocaleChanged = handleLocaleChanged; window.onLocaleChanged = handleLocaleChanged;
ui.window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged; window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation); SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
SystemChannels.system.setMessageHandler(_handleSystemMessage); SystemChannels.system.setMessageHandler(_handleSystemMessage);
} }
...@@ -424,7 +423,7 @@ mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererB ...@@ -424,7 +423,7 @@ mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererB
@protected @protected
@mustCallSuper @mustCallSuper
void handleLocaleChanged() { void handleLocaleChanged() {
dispatchLocalesChanged(ui.window.locales); dispatchLocalesChanged(window.locales);
} }
/// Notify all the observers that the locale has changed (using /// Notify all the observers that the locale has changed (using
......
...@@ -980,10 +980,10 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -980,10 +980,10 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
@override @override
void didChangeMetrics() { void didChangeMetrics() {
if (_lastBottomViewInset < ui.window.viewInsets.bottom) { if (_lastBottomViewInset < WidgetsBinding.instance.window.viewInsets.bottom) {
_showCaretOnScreen(); _showCaretOnScreen();
} }
_lastBottomViewInset = ui.window.viewInsets.bottom; _lastBottomViewInset = WidgetsBinding.instance.window.viewInsets.bottom;
} }
void _formatAndSetValue(TextEditingValue value) { void _formatAndSetValue(TextEditingValue value) {
...@@ -1102,7 +1102,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -1102,7 +1102,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
if (_hasFocus) { if (_hasFocus) {
// Listen for changing viewInsets, which indicates keyboard showing up. // Listen for changing viewInsets, which indicates keyboard showing up.
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
_lastBottomViewInset = ui.window.viewInsets.bottom; _lastBottomViewInset = WidgetsBinding.instance.window.viewInsets.bottom;
_showCaretOnScreen(); _showCaretOnScreen();
if (!_value.selection.isValid) { if (!_value.selection.isValid) {
// Place cursor at the end if the selection is invalid when we receive focus. // Place cursor at the end if the selection is invalid when we receive focus.
......
...@@ -3,12 +3,12 @@ ...@@ -3,12 +3,12 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:math' as math; import 'dart:math' as math;
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/physics.dart'; import 'package:flutter/physics.dart';
import 'binding.dart' show WidgetsBinding;
import 'overscroll_indicator.dart'; import 'overscroll_indicator.dart';
import 'scroll_metrics.dart'; import 'scroll_metrics.dart';
import 'scroll_simulation.dart'; import 'scroll_simulation.dart';
...@@ -174,8 +174,8 @@ class ScrollPhysics { ...@@ -174,8 +174,8 @@ class ScrollPhysics {
static final Tolerance _kDefaultTolerance = Tolerance( static final Tolerance _kDefaultTolerance = Tolerance(
// TODO(ianh): Handle the case of the device pixel ratio changing. // TODO(ianh): Handle the case of the device pixel ratio changing.
// TODO(ianh): Get this from the local MediaQuery not dart:ui's window object. // TODO(ianh): Get this from the local MediaQuery not dart:ui's window object.
velocity: 1.0 / (0.050 * ui.window.devicePixelRatio), // logical pixels per second velocity: 1.0 / (0.050 * WidgetsBinding.instance.window.devicePixelRatio), // logical pixels per second
distance: 1.0 / ui.window.devicePixelRatio // logical pixels distance: 1.0 / WidgetsBinding.instance.window.devicePixelRatio // logical pixels
); );
/// The tolerance to use for ballistic simulations. /// The tolerance to use for ballistic simulations.
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
import 'dart:math' as math; import 'dart:math' as math;
import 'dart:ui' show SemanticsFlag; import 'dart:ui' show SemanticsFlag;
import 'dart:ui' as ui show window;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
...@@ -89,7 +88,7 @@ class _SemanticsDebuggerState extends State<SemanticsDebugger> with WidgetsBindi ...@@ -89,7 +88,7 @@ class _SemanticsDebuggerState extends State<SemanticsDebugger> with WidgetsBindi
Offset _lastPointerDownLocation; Offset _lastPointerDownLocation;
void _handlePointerDown(PointerDownEvent event) { void _handlePointerDown(PointerDownEvent event) {
setState(() { setState(() {
_lastPointerDownLocation = event.position * ui.window.devicePixelRatio; _lastPointerDownLocation = event.position * WidgetsBinding.instance.window.devicePixelRatio;
}); });
// TODO(ianh): Use a gesture recognizer so that we can reset the // TODO(ianh): Use a gesture recognizer so that we can reset the
// _lastPointerDownLocation when none of the other gesture recognizers win. // _lastPointerDownLocation when none of the other gesture recognizers win.
...@@ -150,7 +149,7 @@ class _SemanticsDebuggerState extends State<SemanticsDebugger> with WidgetsBindi ...@@ -150,7 +149,7 @@ class _SemanticsDebuggerState extends State<SemanticsDebugger> with WidgetsBindi
_pipelineOwner, _pipelineOwner,
_client.generation, _client.generation,
_lastPointerDownLocation, // in physical pixels _lastPointerDownLocation, // in physical pixels
ui.window.devicePixelRatio, WidgetsBinding.instance.window.devicePixelRatio,
), ),
child: GestureDetector( child: GestureDetector(
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
......
...@@ -10,7 +10,6 @@ import 'dart:math' as math; ...@@ -10,7 +10,6 @@ import 'dart:math' as math;
import 'dart:typed_data'; import 'dart:typed_data';
import 'dart:ui' as ui import 'dart:ui' as ui
show show
window,
ClipOp, ClipOp,
EngineLayer, EngineLayer,
Image, Image,
...@@ -2294,7 +2293,7 @@ class _WidgetInspectorState extends State<WidgetInspector> ...@@ -2294,7 +2293,7 @@ class _WidgetInspectorState extends State<WidgetInspector>
// on the edge of the display. If the pointer is being dragged off the edge // on the edge of the display. If the pointer is being dragged off the edge
// of the display we do not want to select anything. A user can still select // of the display we do not want to select anything. A user can still select
// a widget that is only at the exact screen margin by tapping. // a widget that is only at the exact screen margin by tapping.
final Rect bounds = (Offset.zero & (ui.window.physicalSize / ui.window.devicePixelRatio)).deflate(_kOffScreenMargin); final Rect bounds = (Offset.zero & (WidgetsBinding.instance.window.physicalSize / WidgetsBinding.instance.window.devicePixelRatio)).deflate(_kOffScreenMargin);
if (!bounds.contains(_lastPointerLocation)) { if (!bounds.contains(_lastPointerLocation)) {
setState(() { setState(() {
selection.clear(); selection.clear();
......
...@@ -44,7 +44,7 @@ void main() { ...@@ -44,7 +44,7 @@ void main() {
expect(offscreen.child.hasSize, isFalse); expect(offscreen.child.hasSize, isFalse);
expect(offscreen.painted, isFalse); expect(offscreen.painted, isFalse);
// Attach the offscreen to a custom render view and owner // Attach the offscreen to a custom render view and owner
final RenderView renderView = RenderView(configuration: testConfiguration); final RenderView renderView = RenderView(configuration: testConfiguration, window: null);
final PipelineOwner pipelineOwner = PipelineOwner(); final PipelineOwner pipelineOwner = PipelineOwner();
renderView.attach(pipelineOwner); renderView.attach(pipelineOwner);
renderView.child = offscreen.root; renderView.child = offscreen.root;
...@@ -73,7 +73,7 @@ void main() { ...@@ -73,7 +73,7 @@ void main() {
expect(offscreen.child.hasSize, isFalse); expect(offscreen.child.hasSize, isFalse);
expect(offscreen.painted, isFalse); expect(offscreen.painted, isFalse);
// Attach the offscreen to a custom render view and owner // Attach the offscreen to a custom render view and owner
final RenderView renderView = RenderView(configuration: testConfiguration); final RenderView renderView = RenderView(configuration: testConfiguration, window: null);
final PipelineOwner pipelineOwner = PipelineOwner(); final PipelineOwner pipelineOwner = PipelineOwner();
renderView.attach(pipelineOwner); renderView.attach(pipelineOwner);
renderView.child = offscreen.root; renderView.child = offscreen.root;
......
...@@ -4,10 +4,14 @@ ...@@ -4,10 +4,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import '../flutter_test_alternative.dart'; import 'package:flutter_test/src/binding.dart' show TestWidgetsFlutterBinding;
import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
test('ensure frame is scheduled for markNeedsSemanticsUpdate', () { test('ensure frame is scheduled for markNeedsSemanticsUpdate', () {
// Initialize all bindings because owner.flushSemantics() requires a window
TestWidgetsFlutterBinding.ensureInitialized();
final TestRenderObject renderObject = TestRenderObject(); final TestRenderObject renderObject = TestRenderObject();
int onNeedVisualUpdateCallCount = 0; int onNeedVisualUpdateCallCount = 0;
final PipelineOwner owner = PipelineOwner(onNeedVisualUpdate: () { final PipelineOwner owner = PipelineOwner(onNeedVisualUpdate: () {
......
...@@ -5,10 +5,13 @@ ...@@ -5,10 +5,13 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import '../flutter_test_alternative.dart'; import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
test('Mock binary message handler control test', () async { test('Mock binary message handler control test', () async {
// Initialize all bindings because BinaryMessages.send() needs a window.
TestWidgetsFlutterBinding.ensureInitialized();
final List<ByteData> log = <ByteData>[]; final List<ByteData> log = <ByteData>[];
BinaryMessages.setMockMessageHandler('test1', (ByteData message) async { BinaryMessages.setMockMessageHandler('test1', (ByteData message) async {
......
...@@ -9,7 +9,10 @@ import 'package:flutter/rendering.dart'; ...@@ -9,7 +9,10 @@ import 'package:flutter/rendering.dart';
const Size _kTestViewSize = Size(800.0, 600.0); const Size _kTestViewSize = Size(800.0, 600.0);
class OffscreenRenderView extends RenderView { class OffscreenRenderView extends RenderView {
OffscreenRenderView() : super(configuration: const ViewConfiguration(size: _kTestViewSize)); OffscreenRenderView() : super(
configuration: const ViewConfiguration(size: _kTestViewSize),
window: WidgetsBinding.instance.window,
);
@override @override
void compositeFrame() { void compositeFrame() {
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' as ui;
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
...@@ -39,10 +37,10 @@ void main() { ...@@ -39,10 +37,10 @@ void main() {
}); });
testWidgets('MediaQueryData is sane', (WidgetTester tester) async { testWidgets('MediaQueryData is sane', (WidgetTester tester) async {
final MediaQueryData data = MediaQueryData.fromWindow(ui.window); final MediaQueryData data = MediaQueryData.fromWindow(WidgetsBinding.instance.window);
expect(data, hasOneLineDescription); expect(data, hasOneLineDescription);
expect(data.hashCode, equals(data.copyWith().hashCode)); expect(data.hashCode, equals(data.copyWith().hashCode));
expect(data.size, equals(ui.window.physicalSize / ui.window.devicePixelRatio)); expect(data.size, equals(WidgetsBinding.instance.window.physicalSize / WidgetsBinding.instance.window.devicePixelRatio));
expect(data.accessibleNavigation, false); expect(data.accessibleNavigation, false);
expect(data.invertColors, false); expect(data.invertColors, false);
expect(data.disableAnimations, false); expect(data.disableAnimations, false);
...@@ -50,7 +48,7 @@ void main() { ...@@ -50,7 +48,7 @@ void main() {
}); });
testWidgets('MediaQueryData.copyWith defaults to source', (WidgetTester tester) async { testWidgets('MediaQueryData.copyWith defaults to source', (WidgetTester tester) async {
final MediaQueryData data = MediaQueryData.fromWindow(ui.window); final MediaQueryData data = MediaQueryData.fromWindow(WidgetsBinding.instance.window);
final MediaQueryData copied = data.copyWith(); final MediaQueryData copied = data.copyWith();
expect(copied.size, data.size); expect(copied.size, data.size);
expect(copied.devicePixelRatio, data.devicePixelRatio); expect(copied.devicePixelRatio, data.devicePixelRatio);
...@@ -65,7 +63,7 @@ void main() { ...@@ -65,7 +63,7 @@ void main() {
}); });
testWidgets('MediaQuery.copyWith copies specified values', (WidgetTester tester) async { testWidgets('MediaQuery.copyWith copies specified values', (WidgetTester tester) async {
final MediaQueryData data = MediaQueryData.fromWindow(ui.window); final MediaQueryData data = MediaQueryData.fromWindow(WidgetsBinding.instance.window);
final MediaQueryData copied = data.copyWith( final MediaQueryData copied = data.copyWith(
size: const Size(3.14, 2.72), size: const Size(3.14, 2.72),
devicePixelRatio: 1.41, devicePixelRatio: 1.41,
......
...@@ -62,3 +62,4 @@ export 'src/test_pointer.dart'; ...@@ -62,3 +62,4 @@ export 'src/test_pointer.dart';
export 'src/test_text_input.dart'; export 'src/test_text_input.dart';
export 'src/test_vsync.dart'; export 'src/test_vsync.dart';
export 'src/widget_tester.dart'; export 'src/widget_tester.dart';
export 'src/window.dart';
...@@ -108,11 +108,11 @@ class MinimumTapTargetGuideline extends AccessibilityGuideline { ...@@ -108,11 +108,11 @@ class MinimumTapTargetGuideline extends AccessibilityGuideline {
const double delta = 0.001; const double delta = 0.001;
if (paintBounds.left <= delta if (paintBounds.left <= delta
|| paintBounds.top <= delta || paintBounds.top <= delta
|| (paintBounds.bottom - ui.window.physicalSize.height).abs() <= delta || (paintBounds.bottom - tester.binding.window.physicalSize.height).abs() <= delta
|| (paintBounds.right - ui.window.physicalSize.width).abs() <= delta) || (paintBounds.right - tester.binding.window.physicalSize.width).abs() <= delta)
return result; return result;
// shrink by device pixel ratio. // shrink by device pixel ratio.
final Size candidateSize = paintBounds.size / ui.window.devicePixelRatio; final Size candidateSize = paintBounds.size / tester.binding.window.devicePixelRatio;
if (candidateSize.width < size.width || candidateSize.height < size.height) if (candidateSize.width < size.width || candidateSize.height < size.height)
result += Evaluation.fail( result += Evaluation.fail(
'$node: expected tap target size of at least $size, but found $candidateSize\n' '$node: expected tap target size of at least $size, but found $candidateSize\n'
......
...@@ -13,6 +13,7 @@ import 'package:flutter/rendering.dart'; ...@@ -13,6 +13,7 @@ import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart' show TestWindow;
import 'package:quiver/testing/async.dart'; import 'package:quiver/testing/async.dart';
import 'package:quiver/time.dart'; import 'package:quiver/time.dart';
import 'package:test_api/test_api.dart' as test_package; import 'package:test_api/test_api.dart' as test_package;
...@@ -96,12 +97,16 @@ abstract class TestWidgetsFlutterBinding extends BindingBase ...@@ -96,12 +97,16 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
/// ///
/// This constructor overrides the [debugPrint] global hook to point to /// This constructor overrides the [debugPrint] global hook to point to
/// [debugPrintOverride], which can be overridden by subclasses. /// [debugPrintOverride], which can be overridden by subclasses.
TestWidgetsFlutterBinding() { TestWidgetsFlutterBinding() : _window = TestWindow(window: ui.window) {
debugPrint = debugPrintOverride; debugPrint = debugPrintOverride;
debugDisableShadows = disableShadows; debugDisableShadows = disableShadows;
debugCheckIntrinsicSizes = checkIntrinsicSizes; debugCheckIntrinsicSizes = checkIntrinsicSizes;
} }
@override
TestWindow get window => _window;
final TestWindow _window;
/// The value to set [debugPrint] to while tests are running. /// The value to set [debugPrint] to while tests are running.
/// ///
/// This can be used to redirect console output from the framework, or to /// This can be used to redirect console output from the framework, or to
...@@ -265,8 +270,8 @@ abstract class TestWidgetsFlutterBinding extends BindingBase ...@@ -265,8 +270,8 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
@override @override
ViewConfiguration createViewConfiguration() { ViewConfiguration createViewConfiguration() {
final double devicePixelRatio = ui.window.devicePixelRatio; final double devicePixelRatio = window.devicePixelRatio;
final Size size = _surfaceSize ?? ui.window.physicalSize / devicePixelRatio; final Size size = _surfaceSize ?? window.physicalSize / devicePixelRatio;
return ViewConfiguration( return ViewConfiguration(
size: size, size: size,
devicePixelRatio: devicePixelRatio, devicePixelRatio: devicePixelRatio,
...@@ -665,8 +670,8 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding { ...@@ -665,8 +670,8 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
@override @override
void initInstances() { void initInstances() {
super.initInstances(); super.initInstances();
ui.window.onBeginFrame = null; window.onBeginFrame = null;
ui.window.onDrawFrame = null; window.onDrawFrame = null;
} }
FakeAsync _currentFakeAsync; // set in runTest; cleared in postTest FakeAsync _currentFakeAsync; // set in runTest; cleared in postTest
...@@ -1145,7 +1150,7 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding { ...@@ -1145,7 +1150,7 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
_pendingFrame = null; _pendingFrame = null;
_expectingFrame = false; _expectingFrame = false;
} else if (framePolicy != LiveTestWidgetsFlutterBindingFramePolicy.benchmark) { } else if (framePolicy != LiveTestWidgetsFlutterBindingFramePolicy.benchmark) {
ui.window.scheduleFrame(); window.scheduleFrame();
} }
} }
...@@ -1155,6 +1160,7 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding { ...@@ -1155,6 +1160,7 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
renderView = _LiveTestRenderView( renderView = _LiveTestRenderView(
configuration: createViewConfiguration(), configuration: createViewConfiguration(),
onNeedPaint: _handleViewNeedsPaint, onNeedPaint: _handleViewNeedsPaint,
window: window,
); );
renderView.scheduleInitialFrame(); renderView.scheduleInitialFrame();
} }
...@@ -1286,7 +1292,10 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding { ...@@ -1286,7 +1292,10 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
@override @override
ViewConfiguration createViewConfiguration() { ViewConfiguration createViewConfiguration() {
return TestViewConfiguration(size: _surfaceSize ?? _kDefaultTestViewportSize); return TestViewConfiguration(
size: _surfaceSize ?? _kDefaultTestViewportSize,
window: window,
);
} }
@override @override
...@@ -1310,15 +1319,18 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding { ...@@ -1310,15 +1319,18 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
/// size onto the actual display using the [BoxFit.contain] algorithm. /// size onto the actual display using the [BoxFit.contain] algorithm.
class TestViewConfiguration extends ViewConfiguration { class TestViewConfiguration extends ViewConfiguration {
/// Creates a [TestViewConfiguration] with the given size. Defaults to 800x600. /// Creates a [TestViewConfiguration] with the given size. Defaults to 800x600.
TestViewConfiguration({ Size size = _kDefaultTestViewportSize }) TestViewConfiguration({
: _paintMatrix = _getMatrix(size, ui.window.devicePixelRatio), Size size = _kDefaultTestViewportSize,
_hitTestMatrix = _getMatrix(size, 1.0), ui.Window window,
})
: _paintMatrix = _getMatrix(size, window.devicePixelRatio, window),
_hitTestMatrix = _getMatrix(size, 1.0, window),
super(size: size); super(size: size);
static Matrix4 _getMatrix(Size size, double devicePixelRatio) { static Matrix4 _getMatrix(Size size, double devicePixelRatio, ui.Window window) {
final double inverseRatio = devicePixelRatio / ui.window.devicePixelRatio; final double inverseRatio = devicePixelRatio / window.devicePixelRatio;
final double actualWidth = ui.window.physicalSize.width * inverseRatio; final double actualWidth = window.physicalSize.width * inverseRatio;
final double actualHeight = ui.window.physicalSize.height * inverseRatio; final double actualHeight = window.physicalSize.height * inverseRatio;
final double desiredWidth = size.width; final double desiredWidth = size.width;
final double desiredHeight = size.height; final double desiredHeight = size.height;
double scale, shiftX, shiftY; double scale, shiftX, shiftY;
...@@ -1377,7 +1389,8 @@ class _LiveTestRenderView extends RenderView { ...@@ -1377,7 +1389,8 @@ class _LiveTestRenderView extends RenderView {
_LiveTestRenderView({ _LiveTestRenderView({
ViewConfiguration configuration, ViewConfiguration configuration,
this.onNeedPaint, this.onNeedPaint,
}) : super(configuration: configuration); @required ui.Window window,
}) : super(configuration: configuration, window: window);
@override @override
TestViewConfiguration get configuration => super.configuration; TestViewConfiguration get configuration => super.configuration;
...@@ -1714,4 +1727,4 @@ class _MockHttpHeaders extends HttpHeaders { ...@@ -1714,4 +1727,4 @@ class _MockHttpHeaders extends HttpHeaders {
@override @override
String value(String name) => null; String value(String name) => null;
} }
\ No newline at end of file
This diff is collapsed.
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' as ui show window;
import 'dart:ui' show Size, Locale, WindowPadding, AccessibilityFeatures;
import 'package:flutter/widgets.dart' show WidgetsBinding;
import 'package:flutter_test/flutter_test.dart';
import 'package:meta/meta.dart';
void main() {
testWidgets('TestWindow can fake device pixel ratio', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<double>(
tester: tester,
realValue: ui.window.devicePixelRatio,
fakeValue: 2.5,
propertyRetriever: () {
return WidgetsBinding.instance.window.devicePixelRatio;
},
propertyFaker: (TestWidgetsFlutterBinding binding, double fakeValue) {
binding.window.devicePixelRatioTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can fake physical size', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<Size>(
tester: tester,
realValue: ui.window.physicalSize,
fakeValue: const Size(50, 50),
propertyRetriever: () {
return WidgetsBinding.instance.window.physicalSize;
},
propertyFaker: (TestWidgetsFlutterBinding binding, Size fakeValue) {
binding.window.physicalSizeTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can fake view insets', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<WindowPadding>(
tester: tester,
realValue: ui.window.viewInsets,
fakeValue: const FakeWindowPadding(),
propertyRetriever: () {
return WidgetsBinding.instance.window.viewInsets;
},
propertyFaker: (TestWidgetsFlutterBinding binding, WindowPadding fakeValue) {
binding.window.viewInsetsTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can fake padding', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<WindowPadding>(
tester: tester,
realValue: ui.window.padding,
fakeValue: const FakeWindowPadding(),
propertyRetriever: () {
return WidgetsBinding.instance.window.padding;
},
propertyFaker: (TestWidgetsFlutterBinding binding, WindowPadding fakeValue) {
binding.window.paddingTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can fake locale', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<Locale>(
tester: tester,
realValue: ui.window.locale,
fakeValue: const Locale('fake_language_code'),
propertyRetriever: () {
return WidgetsBinding.instance.window.locale;
},
propertyFaker: (TestWidgetsFlutterBinding binding, Locale fakeValue) {
binding.window.localeTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can fake locales', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<List<Locale>>(
tester: tester,
realValue: ui.window.locales,
fakeValue: <Locale>[const Locale('fake_language_code')],
propertyRetriever: () {
return WidgetsBinding.instance.window.locales;
},
propertyFaker: (TestWidgetsFlutterBinding binding, List<Locale> fakeValue) {
binding.window.localesTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can fake text scale factor', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<double>(
tester: tester,
realValue: ui.window.textScaleFactor,
fakeValue: 2.5,
propertyRetriever: () {
return WidgetsBinding.instance.window.textScaleFactor;
},
propertyFaker: (TestWidgetsFlutterBinding binding, double fakeValue) {
binding.window.textScaleFactorTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can fake clock format', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<bool>(
tester: tester,
realValue: ui.window.alwaysUse24HourFormat,
fakeValue: !ui.window.alwaysUse24HourFormat,
propertyRetriever: () {
return WidgetsBinding.instance.window.alwaysUse24HourFormat;
},
propertyFaker: (TestWidgetsFlutterBinding binding, bool fakeValue) {
binding.window.alwaysUse24HourFormatTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can fake default route name', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<String>(
tester: tester,
realValue: ui.window.defaultRouteName,
fakeValue: 'fake_route',
propertyRetriever: () {
return WidgetsBinding.instance.window.defaultRouteName;
},
propertyFaker: (TestWidgetsFlutterBinding binding, String fakeValue) {
binding.window.defaultRouteNameTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can fake semantics enabled', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<bool>(
tester: tester,
realValue: ui.window.semanticsEnabled,
fakeValue: !ui.window.semanticsEnabled,
propertyRetriever: () {
return WidgetsBinding.instance.window.semanticsEnabled;
},
propertyFaker: (TestWidgetsFlutterBinding binding, bool fakeValue) {
binding.window.semanticsEnabledTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can fake accessibility features', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<AccessibilityFeatures>(
tester: tester,
realValue: ui.window.accessibilityFeatures,
fakeValue: const FakeAccessibilityFeatures(),
propertyRetriever: () {
return WidgetsBinding.instance.window.accessibilityFeatures;
},
propertyFaker: (TestWidgetsFlutterBinding binding, AccessibilityFeatures fakeValue) {
binding.window.accessibilityFeaturesTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can clear out fake properties all at once', (WidgetTester tester) {
final double originalDevicePixelRatio = ui.window.devicePixelRatio;
final double originalTextScaleFactor = ui.window.textScaleFactor;
final TestWindow testWindow = retrieveTestBinding(tester).window;
// Set fake values for window properties.
testWindow.devicePixelRatioTestValue = 2.5;
testWindow.textScaleFactorTestValue = 3.0;
// Erase fake window property values.
testWindow.clearAllTestValues();
// Verify that the window once again reports real property values.
expect(WidgetsBinding.instance.window.devicePixelRatio, originalDevicePixelRatio);
expect(WidgetsBinding.instance.window.textScaleFactor, originalTextScaleFactor);
});
}
void verifyThatTestWindowCanFakeProperty<WindowPropertyType>({
@required WidgetTester tester,
@required WindowPropertyType realValue,
@required WindowPropertyType fakeValue,
@required WindowPropertyType Function() propertyRetriever,
@required Function(TestWidgetsFlutterBinding, WindowPropertyType fakeValue) propertyFaker,
}) {
WindowPropertyType propertyBeforeFaking;
WindowPropertyType propertyAfterFaking;
propertyBeforeFaking = propertyRetriever();
propertyFaker(retrieveTestBinding(tester), fakeValue);
propertyAfterFaking = propertyRetriever();
expect(propertyBeforeFaking, realValue);
expect(propertyAfterFaking, fakeValue);
}
TestWidgetsFlutterBinding retrieveTestBinding(WidgetTester tester) {
final WidgetsBinding binding = tester.binding;
assert(binding is TestWidgetsFlutterBinding);
final TestWidgetsFlutterBinding testBinding = binding;
return testBinding;
}
class FakeWindowPadding implements WindowPadding {
const FakeWindowPadding({
this.left = 0.0,
this.top = 0.0,
this.right = 0.0,
this.bottom = 0.0,
});
@override
final double left;
@override
final double top;
@override
final double right;
@override
final double bottom;
}
class FakeAccessibilityFeatures implements AccessibilityFeatures {
const FakeAccessibilityFeatures({
this.accessibleNavigation = false,
this.invertColors = false,
this.disableAnimations = false,
this.boldText = false,
this.reduceMotion = false,
});
@override
final bool accessibleNavigation;
@override
final bool invertColors;
@override
final bool disableAnimations;
@override
final bool boldText;
@override
final bool reduceMotion;
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment