mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Enable dependency injection of Window instead of using static property (#27389)
This commit is contained in:
parent
3ba97da9f5
commit
92125ed38f
@ -32,8 +32,14 @@ Future<void> main() async {
|
||||
await tester.pump(); // Start 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 small = TestViewConfiguration(size: const Size(355.0, 635.0));
|
||||
final TestViewConfiguration big = TestViewConfiguration(
|
||||
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;
|
||||
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.benchmark;
|
||||
|
||||
|
@ -6,7 +6,8 @@ import 'dart:async';
|
||||
import 'dart:convert' show json;
|
||||
import 'dart:developer' as developer;
|
||||
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';
|
||||
|
||||
@ -66,6 +67,24 @@ abstract class BindingBase {
|
||||
static bool _debugInitialized = 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 platform and otherwise configure their services. Subclasses must call
|
||||
/// "super.initInstances()".
|
||||
@ -122,7 +141,7 @@ abstract class BindingBase {
|
||||
name: 'saveCompilationTrace',
|
||||
callback: (Map<String, String> parameters) async {
|
||||
return <String, dynamic> {
|
||||
'value': saveCompilationTrace(),
|
||||
'value': ui.saveCompilationTrace(),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:ui' as ui show window, PointerDataPacket;
|
||||
import 'dart:ui' as ui show PointerDataPacket;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
@ -62,7 +62,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
|
||||
void initInstances() {
|
||||
super.initInstances();
|
||||
_instance = this;
|
||||
ui.window.onPointerDataPacket = _handlePointerDataPacket;
|
||||
window.onPointerDataPacket = _handlePointerDataPacket;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -80,7 +80,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
|
||||
void _handlePointerDataPacket(ui.PointerDataPacket packet) {
|
||||
// We convert pointer data to logical pixels so that e.g. the touch slop can be
|
||||
// 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)
|
||||
_flushPointerEventQueue();
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui' as ui show window;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
@ -31,7 +30,7 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
|
||||
onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
|
||||
onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
|
||||
);
|
||||
ui.window
|
||||
window
|
||||
..onMetricsChanged = handleMetricsChanged
|
||||
..onTextScaleFactorChanged = handleTextScaleFactorChanged
|
||||
..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
|
||||
@ -131,7 +130,7 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
|
||||
/// Called automatically when the binding is created.
|
||||
void initRenderView() {
|
||||
assert(renderView == null);
|
||||
renderView = RenderView(configuration: createViewConfiguration());
|
||||
renderView = RenderView(configuration: createViewConfiguration(), window: window);
|
||||
renderView.scheduleInitialFrame();
|
||||
}
|
||||
|
||||
@ -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
|
||||
/// using `flutter run`.
|
||||
ViewConfiguration createViewConfiguration() {
|
||||
final double devicePixelRatio = ui.window.devicePixelRatio;
|
||||
final double devicePixelRatio = window.devicePixelRatio;
|
||||
return ViewConfiguration(
|
||||
size: ui.window.physicalSize / devicePixelRatio,
|
||||
size: window.physicalSize / devicePixelRatio,
|
||||
devicePixelRatio: devicePixelRatio,
|
||||
);
|
||||
}
|
||||
@ -198,12 +197,12 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
|
||||
// the logical coordinates of the event location back to device pixels
|
||||
// here.
|
||||
return renderView.layer
|
||||
.find<MouseTrackerAnnotation>(offset * ui.window.devicePixelRatio);
|
||||
.find<MouseTrackerAnnotation>(offset * window.devicePixelRatio);
|
||||
});
|
||||
}
|
||||
|
||||
void _handleSemanticsEnabledChanged() {
|
||||
setSemanticsEnabled(ui.window.semanticsEnabled);
|
||||
setSemanticsEnabled(window.semanticsEnabled);
|
||||
}
|
||||
|
||||
/// Whether the render tree associated with this binding should produce a tree
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
import 'dart:developer';
|
||||
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/services.dart';
|
||||
@ -56,8 +56,10 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
|
||||
RenderView({
|
||||
RenderBox child,
|
||||
@required ViewConfiguration configuration,
|
||||
@required ui.Window window,
|
||||
}) : assert(configuration != null),
|
||||
_configuration = configuration {
|
||||
_configuration = configuration,
|
||||
_window = window {
|
||||
this.child = child;
|
||||
}
|
||||
|
||||
@ -82,6 +84,8 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
ui.Window _window;
|
||||
|
||||
/// Whether Flutter should automatically compute the desired system UI.
|
||||
///
|
||||
/// 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>
|
||||
final ui.Scene scene = layer.buildScene(builder);
|
||||
if (automaticSystemUiAdjustment)
|
||||
_updateSystemChrome();
|
||||
ui.window.render(scene);
|
||||
_window.render(scene);
|
||||
scene.dispose();
|
||||
assert(() {
|
||||
if (debugRepaintRainbowEnabled || debugRepaintTextRainbowEnabled)
|
||||
@ -209,8 +213,8 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
|
||||
|
||||
void _updateSystemChrome() {
|
||||
final Rect bounds = paintBounds;
|
||||
final Offset top = Offset(bounds.center.dx, ui.window.padding.top / ui.window.devicePixelRatio);
|
||||
final Offset bottom = Offset(bounds.center.dx, bounds.center.dy - ui.window.padding.bottom / 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 - _window.padding.bottom / _window.devicePixelRatio);
|
||||
final SystemUiOverlayStyle upperOverlayStyle = layer.find<SystemUiOverlayStyle>(top);
|
||||
// Only android has a customizable system navigation bar.
|
||||
SystemUiOverlayStyle lowerOverlayStyle;
|
||||
@ -254,10 +258,10 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
|
||||
properties.add(DiagnosticsNode.message('debug mode enabled - ${Platform.operatingSystem}'));
|
||||
return true;
|
||||
}());
|
||||
properties.add(DiagnosticsProperty<Size>('window size', ui.window.physicalSize, tooltip: 'in physical pixels'));
|
||||
properties.add(DoubleProperty('device pixel ratio', ui.window.devicePixelRatio, tooltip: 'physical pixels per logical pixel'));
|
||||
properties.add(DiagnosticsProperty<Size>('window size', _window.physicalSize, tooltip: 'in physical pixels'));
|
||||
properties.add(DoubleProperty('device pixel ratio', _window.devicePixelRatio, tooltip: 'physical pixels per logical pixel'));
|
||||
properties.add(DiagnosticsProperty<ViewConfiguration>('configuration', configuration, tooltip: 'in logical pixels'));
|
||||
if (ui.window.semanticsEnabled)
|
||||
if (_window.semanticsEnabled)
|
||||
properties.add(DiagnosticsNode.message('semantics enabled'));
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:developer';
|
||||
import 'dart:ui' as ui show window;
|
||||
import 'dart:ui' show AppLifecycleState;
|
||||
|
||||
import 'package:collection/collection.dart' show PriorityQueue, HeapPriorityQueue;
|
||||
@ -191,8 +190,8 @@ mixin SchedulerBinding on BindingBase, ServicesBinding {
|
||||
void initInstances() {
|
||||
super.initInstances();
|
||||
_instance = this;
|
||||
ui.window.onBeginFrame = _handleBeginFrame;
|
||||
ui.window.onDrawFrame = _handleDrawFrame;
|
||||
window.onBeginFrame = _handleBeginFrame;
|
||||
window.onDrawFrame = _handleDrawFrame;
|
||||
SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
|
||||
}
|
||||
|
||||
@ -682,7 +681,7 @@ mixin SchedulerBinding on BindingBase, ServicesBinding {
|
||||
debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
|
||||
return true;
|
||||
}());
|
||||
ui.window.scheduleFrame();
|
||||
window.scheduleFrame();
|
||||
_hasScheduledFrame = true;
|
||||
}
|
||||
|
||||
@ -713,7 +712,7 @@ mixin SchedulerBinding on BindingBase, ServicesBinding {
|
||||
debugPrintStack(label: 'scheduleForcedFrame() called. Current phase is $schedulerPhase.');
|
||||
return true;
|
||||
}());
|
||||
ui.window.scheduleFrame();
|
||||
window.scheduleFrame();
|
||||
_hasScheduledFrame = true;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// 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 AccessibilityFeatures, window;
|
||||
import 'dart:ui' as ui show AccessibilityFeatures;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
@ -21,7 +21,7 @@ mixin SemanticsBinding on BindingBase {
|
||||
void initInstances() {
|
||||
super.initInstances();
|
||||
_instance = this;
|
||||
_accessibilityFeatures = ui.window.accessibilityFeatures;
|
||||
_accessibilityFeatures = window.accessibilityFeatures;
|
||||
}
|
||||
|
||||
/// Called when the platform accessibility features change.
|
||||
@ -29,7 +29,7 @@ mixin SemanticsBinding on BindingBase {
|
||||
/// See [Window.onAccessibilityFeaturesChanged].
|
||||
@protected
|
||||
void handleAccessibilityFeaturesChanged() {
|
||||
_accessibilityFeatures = ui.window.accessibilityFeatures;
|
||||
_accessibilityFeatures = window.accessibilityFeatures;
|
||||
}
|
||||
|
||||
/// The currently active set of [AccessibilityFeatures].
|
||||
|
@ -13,6 +13,7 @@ import 'package:flutter/painting.dart' show MatrixUtils, TransformProperty;
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:vector_math/vector_math_64.dart';
|
||||
|
||||
import 'binding.dart' show SemanticsBinding;
|
||||
import 'semantics_event.dart';
|
||||
|
||||
export 'dart:ui' show SemanticsAction;
|
||||
@ -2473,7 +2474,7 @@ class SemanticsOwner extends ChangeNotifier {
|
||||
final CustomSemanticsAction action = CustomSemanticsAction.getAction(actionId);
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,6 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
@ -20,11 +19,16 @@ mixin ServicesBinding on BindingBase {
|
||||
@override
|
||||
void initInstances() {
|
||||
super.initInstances();
|
||||
ui.window
|
||||
_instance = this;
|
||||
window
|
||||
..onPlatformMessage = BinaryMessages.handlePlatformMessage;
|
||||
initLicenses();
|
||||
}
|
||||
|
||||
/// The current [ServicesBinding], if one has been created.
|
||||
static ServicesBinding get instance => _instance;
|
||||
static ServicesBinding _instance;
|
||||
|
||||
/// Adds relevant licenses to the [LicenseRegistry].
|
||||
///
|
||||
/// By default, the [ServicesBinding]'s implementation of [initLicenses] adds
|
||||
|
@ -8,6 +8,7 @@ import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'binding.dart' show ServicesBinding;
|
||||
import 'platform_channel.dart';
|
||||
|
||||
typedef _MessageHandler = Future<ByteData> Function(ByteData message);
|
||||
@ -36,7 +37,7 @@ class BinaryMessages {
|
||||
|
||||
static Future<ByteData> _sendPlatformMessage(String channel, ByteData message) {
|
||||
final Completer<ByteData> completer = Completer<ByteData>();
|
||||
ui.window.sendPlatformMessage(channel, message, (ByteData reply) {
|
||||
ServicesBinding.instance.window.sendPlatformMessage(channel, message, (ByteData reply) {
|
||||
try {
|
||||
completer.complete(reply);
|
||||
} catch (exception, stack) {
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:collection' show HashMap;
|
||||
import 'dart:ui' as ui show window;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
@ -710,7 +709,7 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
|
||||
void initState() {
|
||||
super.initState();
|
||||
_updateNavigator();
|
||||
_locale = _resolveLocales(ui.window.locales, widget.supportedLocales);
|
||||
_locale = _resolveLocales(WidgetsBinding.instance.window.locales, widget.supportedLocales);
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
}
|
||||
|
||||
@ -996,7 +995,7 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
|
||||
@override
|
||||
void didChangeAccessibilityFeatures() {
|
||||
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.
|
||||
});
|
||||
}
|
||||
@ -1007,7 +1006,7 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
|
||||
@override
|
||||
void didChangeMetrics() {
|
||||
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.
|
||||
});
|
||||
}
|
||||
@ -1015,8 +1014,8 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
|
||||
@override
|
||||
void didChangeTextScaleFactor() {
|
||||
setState(() {
|
||||
// The textScaleFactor property of ui.window has changed. We reference
|
||||
// ui.window in our build function, so we need to call setState(), but
|
||||
// The textScaleFactor property of window has changed. We reference
|
||||
// window in our build function, so we need to call setState(), but
|
||||
// we don't need to cache anything locally.
|
||||
});
|
||||
}
|
||||
@ -1077,12 +1076,12 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
|
||||
if (_navigator != null) {
|
||||
navigator = 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
|
||||
// is in [widget.initialRoute].
|
||||
initialRoute: ui.window.defaultRouteName != Navigator.defaultRouteName
|
||||
? ui.window.defaultRouteName
|
||||
: widget.initialRoute ?? ui.window.defaultRouteName,
|
||||
initialRoute: WidgetsBinding.instance.window.defaultRouteName != Navigator.defaultRouteName
|
||||
? WidgetsBinding.instance.window.defaultRouteName
|
||||
: widget.initialRoute ?? WidgetsBinding.instance.window.defaultRouteName,
|
||||
onGenerateRoute: _onGenerateRoute,
|
||||
onUnknownRoute: _onUnknownRoute,
|
||||
observers: widget.navigatorObservers,
|
||||
@ -1183,7 +1182,7 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
|
||||
assert(_debugCheckLocalizations(appLocale));
|
||||
|
||||
return MediaQuery(
|
||||
data: MediaQueryData.fromWindow(ui.window),
|
||||
data: MediaQueryData.fromWindow(WidgetsBinding.instance.window),
|
||||
child: Localizations(
|
||||
locale: appLocale,
|
||||
delegates: _localizationsDelegates.toList(),
|
||||
|
@ -5,7 +5,6 @@
|
||||
import 'dart:async';
|
||||
import 'dart:developer' as developer;
|
||||
import 'dart:ui' show AppLifecycleState, Locale, AccessibilityFeatures;
|
||||
import 'dart:ui' as ui show window;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
@ -141,7 +140,7 @@ abstract class WidgetsBindingObserver {
|
||||
///
|
||||
/// @override
|
||||
/// void didChangeMetrics() {
|
||||
/// setState(() { _lastSize = ui.window.physicalSize; });
|
||||
/// setState(() { _lastSize = WidgetsBinding.instance.window.physicalSize; });
|
||||
/// }
|
||||
///
|
||||
/// @override
|
||||
@ -197,7 +196,7 @@ abstract class WidgetsBindingObserver {
|
||||
///
|
||||
/// @override
|
||||
/// void didChangeTextScaleFactor() {
|
||||
/// setState(() { _lastTextScaleFactor = ui.window.textScaleFactor; });
|
||||
/// setState(() { _lastTextScaleFactor = WidgetsBinding.instance.window.textScaleFactor; });
|
||||
/// }
|
||||
///
|
||||
/// @override
|
||||
@ -250,8 +249,8 @@ mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererB
|
||||
super.initInstances();
|
||||
_instance = this;
|
||||
buildOwner.onBuildScheduled = _handleBuildScheduled;
|
||||
ui.window.onLocaleChanged = handleLocaleChanged;
|
||||
ui.window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
|
||||
window.onLocaleChanged = handleLocaleChanged;
|
||||
window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
|
||||
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
|
||||
SystemChannels.system.setMessageHandler(_handleSystemMessage);
|
||||
}
|
||||
@ -424,7 +423,7 @@ mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererB
|
||||
@protected
|
||||
@mustCallSuper
|
||||
void handleLocaleChanged() {
|
||||
dispatchLocalesChanged(ui.window.locales);
|
||||
dispatchLocalesChanged(window.locales);
|
||||
}
|
||||
|
||||
/// Notify all the observers that the locale has changed (using
|
||||
|
@ -980,10 +980,10 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
||||
|
||||
@override
|
||||
void didChangeMetrics() {
|
||||
if (_lastBottomViewInset < ui.window.viewInsets.bottom) {
|
||||
if (_lastBottomViewInset < WidgetsBinding.instance.window.viewInsets.bottom) {
|
||||
_showCaretOnScreen();
|
||||
}
|
||||
_lastBottomViewInset = ui.window.viewInsets.bottom;
|
||||
_lastBottomViewInset = WidgetsBinding.instance.window.viewInsets.bottom;
|
||||
}
|
||||
|
||||
void _formatAndSetValue(TextEditingValue value) {
|
||||
@ -1102,7 +1102,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
||||
if (_hasFocus) {
|
||||
// Listen for changing viewInsets, which indicates keyboard showing up.
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
_lastBottomViewInset = ui.window.viewInsets.bottom;
|
||||
_lastBottomViewInset = WidgetsBinding.instance.window.viewInsets.bottom;
|
||||
_showCaretOnScreen();
|
||||
if (!_value.selection.isValid) {
|
||||
// Place cursor at the end if the selection is invalid when we receive focus.
|
||||
|
@ -3,12 +3,12 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:math' as math;
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/physics.dart';
|
||||
|
||||
import 'binding.dart' show WidgetsBinding;
|
||||
import 'overscroll_indicator.dart';
|
||||
import 'scroll_metrics.dart';
|
||||
import 'scroll_simulation.dart';
|
||||
@ -174,8 +174,8 @@ class ScrollPhysics {
|
||||
static final Tolerance _kDefaultTolerance = Tolerance(
|
||||
// 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.
|
||||
velocity: 1.0 / (0.050 * ui.window.devicePixelRatio), // logical pixels per second
|
||||
distance: 1.0 / ui.window.devicePixelRatio // logical pixels
|
||||
velocity: 1.0 / (0.050 * WidgetsBinding.instance.window.devicePixelRatio), // logical pixels per second
|
||||
distance: 1.0 / WidgetsBinding.instance.window.devicePixelRatio // logical pixels
|
||||
);
|
||||
|
||||
/// The tolerance to use for ballistic simulations.
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
import 'dart:math' as math;
|
||||
import 'dart:ui' show SemanticsFlag;
|
||||
import 'dart:ui' as ui show window;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
@ -89,7 +88,7 @@ class _SemanticsDebuggerState extends State<SemanticsDebugger> with WidgetsBindi
|
||||
Offset _lastPointerDownLocation;
|
||||
void _handlePointerDown(PointerDownEvent event) {
|
||||
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
|
||||
// _lastPointerDownLocation when none of the other gesture recognizers win.
|
||||
@ -150,7 +149,7 @@ class _SemanticsDebuggerState extends State<SemanticsDebugger> with WidgetsBindi
|
||||
_pipelineOwner,
|
||||
_client.generation,
|
||||
_lastPointerDownLocation, // in physical pixels
|
||||
ui.window.devicePixelRatio,
|
||||
WidgetsBinding.instance.window.devicePixelRatio,
|
||||
),
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
|
@ -10,7 +10,6 @@ import 'dart:math' as math;
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui' as ui
|
||||
show
|
||||
window,
|
||||
ClipOp,
|
||||
EngineLayer,
|
||||
Image,
|
||||
@ -2294,7 +2293,7 @@ class _WidgetInspectorState extends State<WidgetInspector>
|
||||
// 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
|
||||
// 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)) {
|
||||
setState(() {
|
||||
selection.clear();
|
||||
|
@ -44,7 +44,7 @@ void main() {
|
||||
expect(offscreen.child.hasSize, isFalse);
|
||||
expect(offscreen.painted, isFalse);
|
||||
// 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();
|
||||
renderView.attach(pipelineOwner);
|
||||
renderView.child = offscreen.root;
|
||||
@ -73,7 +73,7 @@ void main() {
|
||||
expect(offscreen.child.hasSize, isFalse);
|
||||
expect(offscreen.painted, isFalse);
|
||||
// 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();
|
||||
renderView.attach(pipelineOwner);
|
||||
renderView.child = offscreen.root;
|
||||
|
@ -4,10 +4,14 @@
|
||||
|
||||
import 'package:flutter/material.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() {
|
||||
test('ensure frame is scheduled for markNeedsSemanticsUpdate', () {
|
||||
// Initialize all bindings because owner.flushSemantics() requires a window
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
final TestRenderObject renderObject = TestRenderObject();
|
||||
int onNeedVisualUpdateCallCount = 0;
|
||||
final PipelineOwner owner = PipelineOwner(onNeedVisualUpdate: () {
|
||||
|
@ -5,10 +5,13 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import '../flutter_test_alternative.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
test('Mock binary message handler control test', () async {
|
||||
// Initialize all bindings because BinaryMessages.send() needs a window.
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
final List<ByteData> log = <ByteData>[];
|
||||
|
||||
BinaryMessages.setMockMessageHandler('test1', (ByteData message) async {
|
||||
|
@ -9,7 +9,10 @@ import 'package:flutter/rendering.dart';
|
||||
const Size _kTestViewSize = Size(800.0, 600.0);
|
||||
|
||||
class OffscreenRenderView extends RenderView {
|
||||
OffscreenRenderView() : super(configuration: const ViewConfiguration(size: _kTestViewSize));
|
||||
OffscreenRenderView() : super(
|
||||
configuration: const ViewConfiguration(size: _kTestViewSize),
|
||||
window: WidgetsBinding.instance.window,
|
||||
);
|
||||
|
||||
@override
|
||||
void compositeFrame() {
|
||||
|
@ -2,8 +2,6 @@
|
||||
// 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;
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
@ -39,10 +37,10 @@ void main() {
|
||||
});
|
||||
|
||||
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.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.invertColors, false);
|
||||
expect(data.disableAnimations, false);
|
||||
@ -50,7 +48,7 @@ void main() {
|
||||
});
|
||||
|
||||
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();
|
||||
expect(copied.size, data.size);
|
||||
expect(copied.devicePixelRatio, data.devicePixelRatio);
|
||||
@ -65,7 +63,7 @@ void main() {
|
||||
});
|
||||
|
||||
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(
|
||||
size: const Size(3.14, 2.72),
|
||||
devicePixelRatio: 1.41,
|
||||
|
@ -62,3 +62,4 @@ export 'src/test_pointer.dart';
|
||||
export 'src/test_text_input.dart';
|
||||
export 'src/test_vsync.dart';
|
||||
export 'src/widget_tester.dart';
|
||||
export 'src/window.dart';
|
||||
|
@ -108,11 +108,11 @@ class MinimumTapTargetGuideline extends AccessibilityGuideline {
|
||||
const double delta = 0.001;
|
||||
if (paintBounds.left <= delta
|
||||
|| paintBounds.top <= delta
|
||||
|| (paintBounds.bottom - ui.window.physicalSize.height).abs() <= delta
|
||||
|| (paintBounds.right - ui.window.physicalSize.width).abs() <= delta)
|
||||
|| (paintBounds.bottom - tester.binding.window.physicalSize.height).abs() <= delta
|
||||
|| (paintBounds.right - tester.binding.window.physicalSize.width).abs() <= delta)
|
||||
return result;
|
||||
// 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)
|
||||
result += Evaluation.fail(
|
||||
'$node: expected tap target size of at least $size, but found $candidateSize\n'
|
||||
|
@ -13,6 +13,7 @@ import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart' show TestWindow;
|
||||
import 'package:quiver/testing/async.dart';
|
||||
import 'package:quiver/time.dart';
|
||||
import 'package:test_api/test_api.dart' as test_package;
|
||||
@ -96,12 +97,16 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
|
||||
///
|
||||
/// This constructor overrides the [debugPrint] global hook to point to
|
||||
/// [debugPrintOverride], which can be overridden by subclasses.
|
||||
TestWidgetsFlutterBinding() {
|
||||
TestWidgetsFlutterBinding() : _window = TestWindow(window: ui.window) {
|
||||
debugPrint = debugPrintOverride;
|
||||
debugDisableShadows = disableShadows;
|
||||
debugCheckIntrinsicSizes = checkIntrinsicSizes;
|
||||
}
|
||||
|
||||
@override
|
||||
TestWindow get window => _window;
|
||||
final TestWindow _window;
|
||||
|
||||
/// The value to set [debugPrint] to while tests are running.
|
||||
///
|
||||
/// This can be used to redirect console output from the framework, or to
|
||||
@ -265,8 +270,8 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
|
||||
|
||||
@override
|
||||
ViewConfiguration createViewConfiguration() {
|
||||
final double devicePixelRatio = ui.window.devicePixelRatio;
|
||||
final Size size = _surfaceSize ?? ui.window.physicalSize / devicePixelRatio;
|
||||
final double devicePixelRatio = window.devicePixelRatio;
|
||||
final Size size = _surfaceSize ?? window.physicalSize / devicePixelRatio;
|
||||
return ViewConfiguration(
|
||||
size: size,
|
||||
devicePixelRatio: devicePixelRatio,
|
||||
@ -665,8 +670,8 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
@override
|
||||
void initInstances() {
|
||||
super.initInstances();
|
||||
ui.window.onBeginFrame = null;
|
||||
ui.window.onDrawFrame = null;
|
||||
window.onBeginFrame = null;
|
||||
window.onDrawFrame = null;
|
||||
}
|
||||
|
||||
FakeAsync _currentFakeAsync; // set in runTest; cleared in postTest
|
||||
@ -1145,7 +1150,7 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
_pendingFrame = null;
|
||||
_expectingFrame = false;
|
||||
} else if (framePolicy != LiveTestWidgetsFlutterBindingFramePolicy.benchmark) {
|
||||
ui.window.scheduleFrame();
|
||||
window.scheduleFrame();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1155,6 +1160,7 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
renderView = _LiveTestRenderView(
|
||||
configuration: createViewConfiguration(),
|
||||
onNeedPaint: _handleViewNeedsPaint,
|
||||
window: window,
|
||||
);
|
||||
renderView.scheduleInitialFrame();
|
||||
}
|
||||
@ -1286,7 +1292,10 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
|
||||
@override
|
||||
ViewConfiguration createViewConfiguration() {
|
||||
return TestViewConfiguration(size: _surfaceSize ?? _kDefaultTestViewportSize);
|
||||
return TestViewConfiguration(
|
||||
size: _surfaceSize ?? _kDefaultTestViewportSize,
|
||||
window: window,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -1310,15 +1319,18 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
/// size onto the actual display using the [BoxFit.contain] algorithm.
|
||||
class TestViewConfiguration extends ViewConfiguration {
|
||||
/// Creates a [TestViewConfiguration] with the given size. Defaults to 800x600.
|
||||
TestViewConfiguration({ Size size = _kDefaultTestViewportSize })
|
||||
: _paintMatrix = _getMatrix(size, ui.window.devicePixelRatio),
|
||||
_hitTestMatrix = _getMatrix(size, 1.0),
|
||||
TestViewConfiguration({
|
||||
Size size = _kDefaultTestViewportSize,
|
||||
ui.Window window,
|
||||
})
|
||||
: _paintMatrix = _getMatrix(size, window.devicePixelRatio, window),
|
||||
_hitTestMatrix = _getMatrix(size, 1.0, window),
|
||||
super(size: size);
|
||||
|
||||
static Matrix4 _getMatrix(Size size, double devicePixelRatio) {
|
||||
final double inverseRatio = devicePixelRatio / ui.window.devicePixelRatio;
|
||||
final double actualWidth = ui.window.physicalSize.width * inverseRatio;
|
||||
final double actualHeight = ui.window.physicalSize.height * inverseRatio;
|
||||
static Matrix4 _getMatrix(Size size, double devicePixelRatio, ui.Window window) {
|
||||
final double inverseRatio = devicePixelRatio / window.devicePixelRatio;
|
||||
final double actualWidth = window.physicalSize.width * inverseRatio;
|
||||
final double actualHeight = window.physicalSize.height * inverseRatio;
|
||||
final double desiredWidth = size.width;
|
||||
final double desiredHeight = size.height;
|
||||
double scale, shiftX, shiftY;
|
||||
@ -1377,7 +1389,8 @@ class _LiveTestRenderView extends RenderView {
|
||||
_LiveTestRenderView({
|
||||
ViewConfiguration configuration,
|
||||
this.onNeedPaint,
|
||||
}) : super(configuration: configuration);
|
||||
@required ui.Window window,
|
||||
}) : super(configuration: configuration, window: window);
|
||||
|
||||
@override
|
||||
TestViewConfiguration get configuration => super.configuration;
|
||||
|
303
packages/flutter_test/lib/src/window.dart
Normal file
303
packages/flutter_test/lib/src/window.dart
Normal file
@ -0,0 +1,303 @@
|
||||
// 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:typed_data' show ByteData;
|
||||
import 'dart:ui' hide window;
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
/// [Window] that wraps another [Window] and allows faking of some properties
|
||||
/// for testing purposes.
|
||||
///
|
||||
/// Tests for certain widgets, e.g., [MaterialApp], might require faking certain
|
||||
/// properties of a [Window]. [TestWindow] facilitates the faking of these properties
|
||||
/// by overidding the properties of a real [Window] with desired fake values. The
|
||||
/// binding used within tests, [TestWidgetsFlutterBinding], contains a [TestWindow]
|
||||
/// that is used by all tests.
|
||||
///
|
||||
/// ## Sample Code
|
||||
///
|
||||
/// A test can utilize a [TestWindow] in the following way:
|
||||
///
|
||||
/// ```dart
|
||||
/// testWidgets('your test name here', (WidgetTester tester) async {
|
||||
/// // Retrieve the TestWidgetsFlutterBinding.
|
||||
/// final TestWidgetsFlutterBinding testBinding = tester.binding;
|
||||
///
|
||||
/// // Fake the desired properties of the TestWindow. All code running
|
||||
/// // within this test will perceive the following fake text scale
|
||||
/// // factor as the real text scale factor of the window.
|
||||
/// testBinding.window.textScaleFactorFakeValue = 2.5;
|
||||
///
|
||||
/// // Test code that depends on text scale factor here.
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// The [TestWidgetsFlutterBinding] is recreated for each test and
|
||||
/// therefore any fake values defined in one test will not persist
|
||||
/// to the next.
|
||||
///
|
||||
/// If a test needs to override a real [Window] property and then later
|
||||
/// return to using the real [Window] property, [TestWindow] provides
|
||||
/// methods to clear each individual test value, e.g., [clearLocaleTestValue()].
|
||||
///
|
||||
/// To clear all fake test values in a [TestWindow], consider using [clearAllTestValues()].
|
||||
class TestWindow implements Window {
|
||||
/// Constructs a [TestWindow] that defers all behavior to the given [window] unless
|
||||
/// explicitly overidden for test purposes.
|
||||
TestWindow({
|
||||
@required Window window,
|
||||
}) : _window = window;
|
||||
|
||||
/// The [Window] that is wrapped by this [TestWindow].
|
||||
final Window _window;
|
||||
|
||||
@override
|
||||
double get devicePixelRatio => _devicePixelRatio ?? _window.devicePixelRatio;
|
||||
double _devicePixelRatio;
|
||||
/// Hides the real device pixel ratio and reports the given [devicePixelRatio] instead.
|
||||
set devicePixelRatioTestValue(double devicePixelRatio) {
|
||||
_devicePixelRatio = devicePixelRatio;
|
||||
}
|
||||
/// Deletes any existing test device pixel ratio and returns to using the real device pixel ratio.
|
||||
void clearDevicePixelRatioTestValue() {
|
||||
_devicePixelRatio = null;
|
||||
}
|
||||
|
||||
@override
|
||||
Size get physicalSize => _physicalSizeTestValue ?? _window.physicalSize;
|
||||
Size _physicalSizeTestValue;
|
||||
/// Hides the real physical size and reports the given [physicalSizeTestValue] instead.
|
||||
set physicalSizeTestValue (Size physicalSizeTestValue) {
|
||||
_physicalSizeTestValue = physicalSizeTestValue;
|
||||
}
|
||||
/// Deletes any existing test physical size and returns to using the real physical size.
|
||||
void clearPhysicalSizeTestValue() {
|
||||
_physicalSizeTestValue = null;
|
||||
}
|
||||
|
||||
@override
|
||||
WindowPadding get viewInsets => _viewInsetsTestValue ?? _window.viewInsets;
|
||||
WindowPadding _viewInsetsTestValue;
|
||||
/// Hides the real view insets and reports the given [viewInsetsTestValue] instead.
|
||||
set viewInsetsTestValue(WindowPadding viewInsetsTestValue) {
|
||||
_viewInsetsTestValue = viewInsetsTestValue;
|
||||
}
|
||||
/// Deletes any existing test view insets and returns to using the real view insets.
|
||||
void clearViewInsetsTestValue() {
|
||||
_viewInsetsTestValue = null;
|
||||
}
|
||||
|
||||
@override
|
||||
WindowPadding get padding => _paddingTestValue ?? _window.padding;
|
||||
WindowPadding _paddingTestValue;
|
||||
/// Hides the real padding and reports the given [paddingTestValue] instead.
|
||||
set paddingTestValue(WindowPadding paddingTestValue) {
|
||||
_paddingTestValue = paddingTestValue;
|
||||
}
|
||||
/// Deletes any existing test padding and returns to using the real padding.
|
||||
void clearPaddingTestValue() {
|
||||
_paddingTestValue = null;
|
||||
}
|
||||
|
||||
@override
|
||||
VoidCallback get onMetricsChanged => _window.onMetricsChanged;
|
||||
@override
|
||||
set onMetricsChanged(VoidCallback callback) {
|
||||
_window.onMetricsChanged = callback;
|
||||
}
|
||||
|
||||
@override
|
||||
Locale get locale => _localeTestValue ?? _window.locale;
|
||||
Locale _localeTestValue;
|
||||
/// Hides the real locale and reports the given [localeTestValue] instead.
|
||||
set localeTestValue(Locale localeTestValue) {
|
||||
_localeTestValue = localeTestValue;
|
||||
}
|
||||
/// Deletes any existing test locale and returns to using the real locale.
|
||||
void clearLocaleTestValue() {
|
||||
_localeTestValue = null;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Locale> get locales => _localesTestValue ?? _window.locales;
|
||||
List<Locale> _localesTestValue;
|
||||
/// Hides the real locales and reports the given [localesTestValue] instead.
|
||||
set localesTestValue(List<Locale> localesTestValue) {
|
||||
_localesTestValue = localesTestValue;
|
||||
}
|
||||
/// Deletes any existing test locales and returns to using the real locales.
|
||||
void clearLocalesTestValue() {
|
||||
_localesTestValue = null;
|
||||
}
|
||||
|
||||
@override
|
||||
VoidCallback get onLocaleChanged => _window.onLocaleChanged;
|
||||
@override
|
||||
set onLocaleChanged(VoidCallback callback) {
|
||||
_window.onLocaleChanged = callback;
|
||||
}
|
||||
|
||||
@override
|
||||
double get textScaleFactor => _textScaleFactorTestValue ?? _window.textScaleFactor;
|
||||
double _textScaleFactorTestValue;
|
||||
/// Hides the real text scale factor and reports the given [textScaleFactorTestValue] instead.
|
||||
set textScaleFactorTestValue(double textScaleFactorTestValue) {
|
||||
_textScaleFactorTestValue = textScaleFactorTestValue;
|
||||
}
|
||||
/// Deletes any existing test text scale factor and returns to using the real text scale factor.
|
||||
void clearTextScaleFactorTestValue() {
|
||||
_textScaleFactorTestValue = null;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get alwaysUse24HourFormat => _alwaysUse24HourFormatTestValue ?? _window.alwaysUse24HourFormat;
|
||||
bool _alwaysUse24HourFormatTestValue;
|
||||
/// Hides the real clock format and reports the given [alwaysUse24HourFormatTestValue] instead.
|
||||
set alwaysUse24HourFormatTestValue(bool alwaysUse24HourFormatTestValue) {
|
||||
_alwaysUse24HourFormatTestValue = alwaysUse24HourFormatTestValue;
|
||||
}
|
||||
/// Deletes any existing test clock format and returns to using the real clock format.
|
||||
void clearAlwaysUse24HourTestValue() {
|
||||
_alwaysUse24HourFormatTestValue = null;
|
||||
}
|
||||
|
||||
@override
|
||||
VoidCallback get onTextScaleFactorChanged => _window.onTextScaleFactorChanged;
|
||||
@override
|
||||
set onTextScaleFactorChanged(VoidCallback callback) {
|
||||
_window.onTextScaleFactorChanged = callback;
|
||||
}
|
||||
|
||||
@override
|
||||
FrameCallback get onBeginFrame => _window.onBeginFrame;
|
||||
@override
|
||||
set onBeginFrame(FrameCallback callback) {
|
||||
_window.onBeginFrame = callback;
|
||||
}
|
||||
|
||||
@override
|
||||
VoidCallback get onDrawFrame => _window.onDrawFrame;
|
||||
@override
|
||||
set onDrawFrame(VoidCallback callback) {
|
||||
_window.onDrawFrame = callback;
|
||||
}
|
||||
|
||||
@override
|
||||
PointerDataPacketCallback get onPointerDataPacket => _window.onPointerDataPacket;
|
||||
@override
|
||||
set onPointerDataPacket(PointerDataPacketCallback callback) {
|
||||
_window.onPointerDataPacket = callback;
|
||||
}
|
||||
|
||||
@override
|
||||
String get defaultRouteName => _defaultRouteNameTestValue ?? _window.defaultRouteName;
|
||||
String _defaultRouteNameTestValue;
|
||||
/// Hides the real default route name and reports the given [defaultRouteNameTestValue] instead.
|
||||
set defaultRouteNameTestValue(String defaultRouteNameTestValue) {
|
||||
_defaultRouteNameTestValue = defaultRouteNameTestValue;
|
||||
}
|
||||
/// Deletes any existing test default route name and returns to using the real default route name.
|
||||
void clearDefaultRouteNameTestValue() {
|
||||
_defaultRouteNameTestValue = null;
|
||||
}
|
||||
|
||||
@override
|
||||
void scheduleFrame() {
|
||||
_window.scheduleFrame();
|
||||
}
|
||||
|
||||
@override
|
||||
void render(Scene scene) {
|
||||
_window.render(scene);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get semanticsEnabled => _semanticsEnabledTestValue ?? _window.semanticsEnabled;
|
||||
bool _semanticsEnabledTestValue;
|
||||
/// Hides the real semantics enabled and reports the given [semanticsEnabledTestValue] instead.
|
||||
set semanticsEnabledTestValue(bool semanticsEnabledTestValue) {
|
||||
_semanticsEnabledTestValue = semanticsEnabledTestValue;
|
||||
}
|
||||
/// Deletes any existing test semantics enabled and returns to using the real semantics enabled.
|
||||
void clearSemanticsEnabledTestValue() {
|
||||
_semanticsEnabledTestValue = null;
|
||||
}
|
||||
|
||||
@override
|
||||
VoidCallback get onSemanticsEnabledChanged => _window.onSemanticsEnabledChanged;
|
||||
@override
|
||||
set onSemanticsEnabledChanged(VoidCallback callback) {
|
||||
_window.onSemanticsEnabledChanged = callback;
|
||||
}
|
||||
|
||||
@override
|
||||
SemanticsActionCallback get onSemanticsAction => _window.onSemanticsAction;
|
||||
@override
|
||||
set onSemanticsAction(SemanticsActionCallback callback) {
|
||||
_window.onSemanticsAction = callback;
|
||||
}
|
||||
|
||||
@override
|
||||
AccessibilityFeatures get accessibilityFeatures => _accessibilityFeaturesTestValue ?? _window.accessibilityFeatures;
|
||||
AccessibilityFeatures _accessibilityFeaturesTestValue;
|
||||
/// Hides the real accessibility features and reports the given [accessibilityFeaturesTestValue] instead.
|
||||
set accessibilityFeaturesTestValue(AccessibilityFeatures accessibilityFeaturesTestValue) {
|
||||
_accessibilityFeaturesTestValue = accessibilityFeaturesTestValue;
|
||||
}
|
||||
/// Deletes any existing test accessibility features and returns to using the real accessibility features.
|
||||
void clearAccessibilityFeaturesTestValue() {
|
||||
_accessibilityFeaturesTestValue = null;
|
||||
}
|
||||
|
||||
@override
|
||||
VoidCallback get onAccessibilityFeaturesChanged => _window.onAccessibilityFeaturesChanged;
|
||||
@override
|
||||
set onAccessibilityFeaturesChanged(VoidCallback callback) {
|
||||
_window.onAccessibilityFeaturesChanged = callback;
|
||||
}
|
||||
|
||||
@override
|
||||
void updateSemantics(SemanticsUpdate update) {
|
||||
_window.updateSemantics(update);
|
||||
}
|
||||
|
||||
@override
|
||||
void setIsolateDebugName(String name) {
|
||||
_window.setIsolateDebugName(name);
|
||||
}
|
||||
|
||||
@override
|
||||
void sendPlatformMessage(String name,
|
||||
ByteData data,
|
||||
PlatformMessageResponseCallback callback) {
|
||||
_window.sendPlatformMessage(name, data, callback);
|
||||
}
|
||||
|
||||
@override
|
||||
PlatformMessageCallback get onPlatformMessage => _window.onPlatformMessage;
|
||||
@override
|
||||
set onPlatformMessage(PlatformMessageCallback callback) {
|
||||
_window.onPlatformMessage = callback;
|
||||
}
|
||||
|
||||
/// Delete any test value properties that have been set on this [TestWindow]
|
||||
/// and return to reporting the real [Window] values for all [Window] properties.
|
||||
///
|
||||
/// If desired, clearing of properties can be done on an individual basis, e.g.,
|
||||
/// [clearLocaleTestValue()].
|
||||
void clearAllTestValues() {
|
||||
clearAccessibilityFeaturesTestValue();
|
||||
clearAlwaysUse24HourTestValue();
|
||||
clearDefaultRouteNameTestValue();
|
||||
clearDevicePixelRatioTestValue();
|
||||
clearLocaleTestValue();
|
||||
clearLocalesTestValue();
|
||||
clearPaddingTestValue();
|
||||
clearPhysicalSizeTestValue();
|
||||
clearSemanticsEnabledTestValue();
|
||||
clearTextScaleFactorTestValue();
|
||||
clearViewInsetsTestValue();
|
||||
}
|
||||
}
|
256
packages/flutter_test/test/window_test.dart
Normal file
256
packages/flutter_test/test/window_test.dart
Normal file
@ -0,0 +1,256 @@
|
||||
// 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user