Unverified Commit 4de692a2 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Add AccessibilityFeatures to media query and fix Snackbar a11y behavior (#19336)

parent 83f3b7db
...@@ -1162,12 +1162,18 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin { ...@@ -1162,12 +1162,18 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
assert(reason != null); assert(reason != null);
if (_snackBars.isEmpty || _snackBarController.status == AnimationStatus.dismissed) if (_snackBars.isEmpty || _snackBarController.status == AnimationStatus.dismissed)
return; return;
final MediaQueryData mediaQuery = MediaQuery.of(context);
final Completer<SnackBarClosedReason> completer = _snackBars.first._completer; final Completer<SnackBarClosedReason> completer = _snackBars.first._completer;
_snackBarController.reverse().then<void>((Null _) { if (mediaQuery.accessibleNavigation) {
assert(mounted); _snackBarController.value = 0.0;
if (!completer.isCompleted) completer.complete(reason);
completer.complete(reason); } else {
}); _snackBarController.reverse().then<void>((Null _) {
assert(mounted);
if (!completer.isCompleted)
completer.complete(reason);
});
}
_snackBarTimer?.cancel(); _snackBarTimer?.cancel();
_snackBarTimer = null; _snackBarTimer = null;
} }
...@@ -1480,12 +1486,19 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin { ...@@ -1480,12 +1486,19 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
if (_snackBars.isNotEmpty) { if (_snackBars.isNotEmpty) {
final ModalRoute<dynamic> route = ModalRoute.of(context); final ModalRoute<dynamic> route = ModalRoute.of(context);
if (route == null || route.isCurrent) { if (route == null || route.isCurrent) {
if (_snackBarController.isCompleted && _snackBarTimer == null) if (_snackBarController.isCompleted && _snackBarTimer == null) {
_snackBarTimer = new Timer(_snackBars.first._widget.duration, () { final SnackBar snackBar = _snackBars.first._widget;
_snackBarTimer = new Timer.periodic(snackBar.duration, (Timer timer) {
assert(_snackBarController.status == AnimationStatus.forward || assert(_snackBarController.status == AnimationStatus.forward ||
_snackBarController.status == AnimationStatus.completed); _snackBarController.status == AnimationStatus.completed);
// Look up MediaQuery again in case the setting changed.
final MediaQueryData mediaQuery = MediaQuery.of(context);
if (mediaQuery.accessibleNavigation && snackBar.action != null)
return;
timer.cancel();
hideCurrentSnackBar(reason: SnackBarClosedReason.timeout); hideCurrentSnackBar(reason: SnackBarClosedReason.timeout);
}); });
}
} else { } else {
_snackBarTimer?.cancel(); _snackBarTimer?.cancel();
_snackBarTimer = null; _snackBarTimer = null;
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +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 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'button_theme.dart'; import 'button_theme.dart';
...@@ -46,6 +47,9 @@ enum SnackBarClosedReason { ...@@ -46,6 +47,9 @@ enum SnackBarClosedReason {
/// The snack bar was closed after the user tapped a [SnackBarAction]. /// The snack bar was closed after the user tapped a [SnackBarAction].
action, action,
/// The snack bar was closed through a [SemanticAction.dismiss].
dismiss,
/// The snack bar was closed by a user's swipe. /// The snack bar was closed by a user's swipe.
swipe, swipe,
...@@ -126,6 +130,9 @@ class _SnackBarActionState extends State<SnackBarAction> { ...@@ -126,6 +130,9 @@ class _SnackBarActionState extends State<SnackBarAction> {
/// ///
/// To control how long the [SnackBar] remains visible, specify a [duration]. /// To control how long the [SnackBar] remains visible, specify a [duration].
/// ///
/// A SnackBar with an action will not time out when TalkBack or VoiceOver are
/// enabled. This is controlled by [AccessibilityFeatures.accessibleNavigation].
///
/// See also: /// See also:
/// ///
/// * [Scaffold.of], to obtain the current [ScaffoldState], which manages the /// * [Scaffold.of], to obtain the current [ScaffoldState], which manages the
...@@ -183,6 +190,7 @@ class SnackBar extends StatelessWidget { ...@@ -183,6 +190,7 @@ class SnackBar extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final MediaQueryData mediaQueryData = MediaQuery.of(context);
assert(animation != null); assert(animation != null);
final ThemeData theme = Theme.of(context); final ThemeData theme = Theme.of(context);
final ThemeData darkTheme = new ThemeData( final ThemeData darkTheme = new ThemeData(
...@@ -213,8 +221,41 @@ class SnackBar extends StatelessWidget { ...@@ -213,8 +221,41 @@ class SnackBar extends StatelessWidget {
} }
final CurvedAnimation heightAnimation = new CurvedAnimation(parent: animation, curve: _snackBarHeightCurve); final CurvedAnimation heightAnimation = new CurvedAnimation(parent: animation, curve: _snackBarHeightCurve);
final CurvedAnimation fadeAnimation = new CurvedAnimation(parent: animation, curve: _snackBarFadeCurve, reverseCurve: const Threshold(0.0)); final CurvedAnimation fadeAnimation = new CurvedAnimation(parent: animation, curve: _snackBarFadeCurve, reverseCurve: const Threshold(0.0));
Widget snackbar = new SafeArea(
top: false,
child: new Row(
children: children,
crossAxisAlignment: CrossAxisAlignment.center,
),
);
snackbar = new Semantics(
container: true,
liveRegion: true,
onDismiss: () {
Scaffold.of(context).removeCurrentSnackBar(reason: SnackBarClosedReason.dismiss);
},
child: new Dismissible(
key: const Key('dismissible'),
direction: DismissDirection.down,
resizeDuration: null,
onDismissed: (DismissDirection direction) {
Scaffold.of(context).removeCurrentSnackBar(reason: SnackBarClosedReason.swipe);
},
child: new Material(
elevation: 6.0,
color: backgroundColor ?? _kSnackBackground,
child: new Theme(
data: darkTheme,
child: mediaQueryData.accessibleNavigation ? snackbar : new FadeTransition(
opacity: fadeAnimation,
child: snackbar,
),
),
),
),
);
return new ClipRect( return new ClipRect(
child: new AnimatedBuilder( child: mediaQueryData.accessibleNavigation ? snackbar : new AnimatedBuilder(
animation: heightAnimation, animation: heightAnimation,
builder: (BuildContext context, Widget child) { builder: (BuildContext context, Widget child) {
return new Align( return new Align(
...@@ -223,38 +264,7 @@ class SnackBar extends StatelessWidget { ...@@ -223,38 +264,7 @@ class SnackBar extends StatelessWidget {
child: child, child: child,
); );
}, },
child: new Semantics( child: snackbar,
liveRegion: true,
container: true,
onDismiss: () {
Scaffold.of(context).removeCurrentSnackBar(reason: SnackBarClosedReason.swipe);
},
child: new Dismissible(
key: const Key('dismissible'),
direction: DismissDirection.down,
resizeDuration: null,
onDismissed: (DismissDirection direction) {
Scaffold.of(context).removeCurrentSnackBar(reason: SnackBarClosedReason.swipe);
},
child: new Material(
elevation: 6.0,
color: backgroundColor ?? _kSnackBackground,
child: new Theme(
data: darkTheme,
child: new FadeTransition(
opacity: fadeAnimation,
child: new SafeArea(
top: false,
child: new Row(
children: children,
crossAxisAlignment: CrossAxisAlignment.center,
),
),
),
),
),
),
),
), ),
); );
} }
......
...@@ -153,6 +153,12 @@ abstract class RendererBinding extends BindingBase with ServicesBinding, Schedul ...@@ -153,6 +153,12 @@ abstract class RendererBinding extends BindingBase with ServicesBinding, Schedul
@protected @protected
void handleTextScaleFactorChanged() { } void handleTextScaleFactorChanged() { }
/// Called when the platform accessibility features change.
///
/// See [Window.onAccessibilityFeaturesChanged].
@protected
void handleAccessibilityFeaturesChanged() {}
/// Returns a [ViewConfiguration] configured for the [RenderView] based on the /// Returns a [ViewConfiguration] configured for the [RenderView] based on the
/// current environment. /// current environment.
/// ///
......
...@@ -527,6 +527,16 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv ...@@ -527,6 +527,16 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
yield DefaultWidgetsLocalizations.delegate; yield DefaultWidgetsLocalizations.delegate;
} }
// ACCESSIBILITY
@override
void didChangeAccessibilityFeatures() {
setState(() {
// The properties of ui.window have changed. We use them in our build
// function, so we need setState(), but we don't cache anything locally.
});
}
// METRICS // METRICS
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:developer' as developer; import 'dart:developer' as developer;
import 'dart:ui' show AppLifecycleState, Locale; import 'dart:ui' show AppLifecycleState, Locale, AccessibilityFeatures;
import 'dart:ui' as ui show window; import 'dart:ui' as ui show window;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
...@@ -229,6 +229,12 @@ abstract class WidgetsBindingObserver { ...@@ -229,6 +229,12 @@ abstract class WidgetsBindingObserver {
/// This method exposes the `memoryPressure` notification from /// This method exposes the `memoryPressure` notification from
/// [SystemChannels.system]. /// [SystemChannels.system].
void didHaveMemoryPressure() { } void didHaveMemoryPressure() { }
/// Called when the system changes the set of currently active accessibility
/// features.
///
/// This method exposes notifications from [Window.onAccessibilityFeaturesChanged].
void didChangeAccessibilityFeatures() {}
} }
/// The glue between the widgets layer and the Flutter engine. /// The glue between the widgets layer and the Flutter engine.
...@@ -243,6 +249,7 @@ abstract class WidgetsBinding extends BindingBase with SchedulerBinding, Gesture ...@@ -243,6 +249,7 @@ abstract class WidgetsBinding extends BindingBase with SchedulerBinding, Gesture
_instance = this; _instance = this;
buildOwner.onBuildScheduled = _handleBuildScheduled; buildOwner.onBuildScheduled = _handleBuildScheduled;
ui.window.onLocaleChanged = handleLocaleChanged; ui.window.onLocaleChanged = handleLocaleChanged;
ui.window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation); SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
SystemChannels.system.setMessageHandler(_handleSystemMessage); SystemChannels.system.setMessageHandler(_handleSystemMessage);
} }
...@@ -368,6 +375,13 @@ abstract class WidgetsBinding extends BindingBase with SchedulerBinding, Gesture ...@@ -368,6 +375,13 @@ abstract class WidgetsBinding extends BindingBase with SchedulerBinding, Gesture
observer.didChangeTextScaleFactor(); observer.didChangeTextScaleFactor();
} }
@override
void handleAccessibilityFeaturesChanged() {
super.handleAccessibilityFeaturesChanged();
for (WidgetsBindingObserver observer in _observers)
observer.didChangeAccessibilityFeatures();
}
/// Called when the system locale changes. /// Called when the system locale changes.
/// ///
/// Calls [dispatchLocaleChanged] to notify the binding observers. /// Calls [dispatchLocaleChanged] to notify the binding observers.
...@@ -392,6 +406,19 @@ abstract class WidgetsBinding extends BindingBase with SchedulerBinding, Gesture ...@@ -392,6 +406,19 @@ abstract class WidgetsBinding extends BindingBase with SchedulerBinding, Gesture
observer.didChangeLocale(locale); observer.didChangeLocale(locale);
} }
/// Notify all the observers that the active set of [AccessibilityFeatures]
/// has changed (using [WidgetsBindingObserver.didChangeAccessibilityFeatures]),
/// giving them the `features` argument.
///
/// This is called by [handleAccessibilityFeaturesChanged] when the
/// [Window.onAccessibilityFeaturesChanged] notification is recieved.
@protected
@mustCallSuper
void dispatchAccessibilityFeaturesChanged() {
for (WidgetsBindingObserver observer in _observers)
observer.didChangeAccessibilityFeatures();
}
/// Called when the system pops the current route. /// Called when the system pops the current route.
/// ///
/// This first notifies the binding observers (using /// This first notifies the binding observers (using
......
...@@ -43,6 +43,9 @@ class MediaQueryData { ...@@ -43,6 +43,9 @@ class MediaQueryData {
this.padding = EdgeInsets.zero, this.padding = EdgeInsets.zero,
this.viewInsets = EdgeInsets.zero, this.viewInsets = EdgeInsets.zero,
this.alwaysUse24HourFormat = false, this.alwaysUse24HourFormat = false,
this.accessibleNavigation = false,
this.invertColors = false,
this.disableAnimations = false,
}); });
/// Creates data for a media query based on the given window. /// Creates data for a media query based on the given window.
...@@ -57,6 +60,9 @@ class MediaQueryData { ...@@ -57,6 +60,9 @@ class MediaQueryData {
textScaleFactor = window.textScaleFactor, textScaleFactor = window.textScaleFactor,
padding = new EdgeInsets.fromWindowPadding(window.padding, window.devicePixelRatio), padding = new EdgeInsets.fromWindowPadding(window.padding, window.devicePixelRatio),
viewInsets = new EdgeInsets.fromWindowPadding(window.viewInsets, window.devicePixelRatio), viewInsets = new EdgeInsets.fromWindowPadding(window.viewInsets, window.devicePixelRatio),
accessibleNavigation = window.accessibilityFeatures.accessibleNavigation,
invertColors = window.accessibilityFeatures.accessibleNavigation,
disableAnimations = window.accessibilityFeatures.disableAnimations,
alwaysUse24HourFormat = window.alwaysUse24HourFormat; alwaysUse24HourFormat = window.alwaysUse24HourFormat;
/// The size of the media in logical pixel (e.g, the size of the screen). /// The size of the media in logical pixel (e.g, the size of the screen).
...@@ -120,6 +126,33 @@ class MediaQueryData { ...@@ -120,6 +126,33 @@ class MediaQueryData {
/// formatting. /// formatting.
final bool alwaysUse24HourFormat; final bool alwaysUse24HourFormat;
/// Whether the user is using an accessibility service like TalkBack or
/// VoiceOver to interact with the application.
///
/// When this setting is true, features such as timeouts should be disabled or
/// have minimum durations increased.
///
/// See also:
///
/// * [Window.AccessibilityFeatures], where the setting originates.
final bool accessibleNavigation;
/// Whether the device is inverting the colors of the platform.
///
/// This flag is currently only updated on iOS devices.
///
/// See also:
///
/// * [Window.AccessibilityFeatures], where the setting originates.
final bool invertColors;
/// Whether the platform is requesting that animations be disabled or reduced
/// as much as possible.
///
/// * [Window.AccessibilityFeatures], where the setting originates.
///
final bool disableAnimations;
/// The orientation of the media (e.g., whether the device is in landscape or portrait mode). /// The orientation of the media (e.g., whether the device is in landscape or portrait mode).
Orientation get orientation { Orientation get orientation {
return size.width > size.height ? Orientation.landscape : Orientation.portrait; return size.width > size.height ? Orientation.landscape : Orientation.portrait;
...@@ -134,6 +167,9 @@ class MediaQueryData { ...@@ -134,6 +167,9 @@ class MediaQueryData {
EdgeInsets padding, EdgeInsets padding,
EdgeInsets viewInsets, EdgeInsets viewInsets,
bool alwaysUse24HourFormat, bool alwaysUse24HourFormat,
bool disableAnimations,
bool invertColors,
bool accessibleNavigation,
}) { }) {
return new MediaQueryData( return new MediaQueryData(
size: size ?? this.size, size: size ?? this.size,
...@@ -142,6 +178,9 @@ class MediaQueryData { ...@@ -142,6 +178,9 @@ class MediaQueryData {
padding: padding ?? this.padding, padding: padding ?? this.padding,
viewInsets: viewInsets ?? this.viewInsets, viewInsets: viewInsets ?? this.viewInsets,
alwaysUse24HourFormat: alwaysUse24HourFormat ?? this.alwaysUse24HourFormat, alwaysUse24HourFormat: alwaysUse24HourFormat ?? this.alwaysUse24HourFormat,
invertColors: invertColors ?? this.invertColors,
disableAnimations: disableAnimations ?? this.disableAnimations,
accessibleNavigation: accessibleNavigation ?? this.accessibleNavigation,
); );
} }
...@@ -179,6 +218,9 @@ class MediaQueryData { ...@@ -179,6 +218,9 @@ class MediaQueryData {
), ),
viewInsets: viewInsets, viewInsets: viewInsets,
alwaysUse24HourFormat: alwaysUse24HourFormat, alwaysUse24HourFormat: alwaysUse24HourFormat,
disableAnimations: disableAnimations,
invertColors: invertColors,
accessibleNavigation: accessibleNavigation,
); );
} }
...@@ -214,6 +256,9 @@ class MediaQueryData { ...@@ -214,6 +256,9 @@ class MediaQueryData {
bottom: removeBottom ? 0.0 : null, bottom: removeBottom ? 0.0 : null,
), ),
alwaysUse24HourFormat: alwaysUse24HourFormat, alwaysUse24HourFormat: alwaysUse24HourFormat,
disableAnimations: disableAnimations,
invertColors: invertColors,
accessibleNavigation: accessibleNavigation,
); );
} }
...@@ -227,11 +272,26 @@ class MediaQueryData { ...@@ -227,11 +272,26 @@ class MediaQueryData {
&& typedOther.textScaleFactor == textScaleFactor && typedOther.textScaleFactor == textScaleFactor
&& typedOther.padding == padding && typedOther.padding == padding
&& typedOther.viewInsets == viewInsets && typedOther.viewInsets == viewInsets
&& typedOther.alwaysUse24HourFormat == alwaysUse24HourFormat; && typedOther.alwaysUse24HourFormat == alwaysUse24HourFormat
&& typedOther.disableAnimations == disableAnimations
&& typedOther.invertColors == invertColors
&& typedOther.accessibleNavigation == accessibleNavigation;
} }
@override @override
int get hashCode => hashValues(size, devicePixelRatio, textScaleFactor, padding, viewInsets, alwaysUse24HourFormat); int get hashCode {
return hashValues(
size,
devicePixelRatio,
textScaleFactor,
padding,
viewInsets,
alwaysUse24HourFormat,
disableAnimations,
invertColors,
accessibleNavigation,
);
}
@override @override
String toString() { String toString() {
...@@ -241,7 +301,10 @@ class MediaQueryData { ...@@ -241,7 +301,10 @@ class MediaQueryData {
'textScaleFactor: $textScaleFactor, ' 'textScaleFactor: $textScaleFactor, '
'padding: $padding, ' 'padding: $padding, '
'viewInsets: $viewInsets, ' 'viewInsets: $viewInsets, '
'alwaysUse24HourFormat: $alwaysUse24HourFormat' 'alwaysUse24HourFormat: $alwaysUse24HourFormat, '
'accessibleNavigation: $accessibleNavigation'
'disableAnimations: $disableAnimations'
'invertColors: $invertColors'
')'; ')';
} }
} }
......
...@@ -482,6 +482,90 @@ void main() { ...@@ -482,6 +482,90 @@ void main() {
expect(closedReason, equals(SnackBarClosedReason.timeout)); expect(closedReason, equals(SnackBarClosedReason.timeout));
}); });
testWidgets('accessible navigation behavior with action', (WidgetTester tester) async {
final GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
await tester.pumpWidget(new MaterialApp(
home: new MediaQuery(
data: const MediaQueryData(accessibleNavigation: true),
child: Scaffold(
key: scaffoldKey,
body: new Builder(
builder: (BuildContext context) {
return new GestureDetector(
onTap: () {
Scaffold.of(context).showSnackBar(new SnackBar(
content: const Text('snack'),
duration: const Duration(seconds: 1),
action: new SnackBarAction(
label: 'ACTION',
onPressed: () {}
),
));
},
child: const Text('X')
);
},
)
)
)
));
await tester.tap(find.text('X'));
await tester.pump();
// Find action immediately
expect(find.text('ACTION'), findsOneWidget);
// Snackbar doesn't close
await tester.pump(const Duration(seconds: 10));
expect(find.text('ACTION'), findsOneWidget);
await tester.tap(find.text('ACTION'));
await tester.pump();
// Snackbar closes immediately
expect(find.text('ACTION'), findsNothing);
});
testWidgets('contributes dismiss semantics', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
final GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
await tester.pumpWidget(new MaterialApp(
home: new MediaQuery(
data: const MediaQueryData(accessibleNavigation: true),
child: Scaffold(
key: scaffoldKey,
body: new Builder(
builder: (BuildContext context) {
return new GestureDetector(
onTap: () {
Scaffold.of(context).showSnackBar(new SnackBar(
content: const Text('snack'),
duration: const Duration(seconds: 1),
action: new SnackBarAction(
label: 'ACTION',
onPressed: () {}
),
));
},
child: const Text('X')
);
},
)
)
)
));
await tester.tap(find.text('X'));
await tester.pumpAndSettle();
expect(tester.getSemanticsData(find.text('snack')), matchesSemanticsData(
isLiveRegion: true,
hasDismissAction: true,
hasScrollDownAction: true,
hasScrollUpAction: true,
label: 'snack',
textDirection: TextDirection.ltr,
));
handle.dispose();
});
testWidgets('SnackBar default display duration test', (WidgetTester tester) async { testWidgets('SnackBar default display duration test', (WidgetTester tester) async {
const String helloSnackBar = 'Hello SnackBar'; const String helloSnackBar = 'Hello SnackBar';
const Key tapTarget = Key('tap-target'); const Key tapTarget = Key('tap-target');
...@@ -530,4 +614,52 @@ void main() { ...@@ -530,4 +614,52 @@ void main() {
expect(find.text(helloSnackBar), findsNothing); expect(find.text(helloSnackBar), findsNothing);
}); });
testWidgets('SnackBar handles updates to accessibleNavigation', (WidgetTester tester) async {
Future<void> boilerplate({bool accessibleNavigation}) {
return tester.pumpWidget(new MaterialApp(
home: new MediaQuery(
data: new MediaQueryData(accessibleNavigation: accessibleNavigation),
child: new Scaffold(
body: new Builder(
builder: (BuildContext context) {
return new GestureDetector(
onTap: () {
Scaffold.of(context).showSnackBar(new SnackBar(
content: const Text('test'),
action: new SnackBarAction(label: 'foo', onPressed: () {}),
));
},
behavior: HitTestBehavior.opaque,
child: const Text('X'),
);
}
)
)
)
));
}
await boilerplate(accessibleNavigation: false);
expect(find.text('test'), findsNothing);
await tester.tap(find.text('X'));
await tester.pump(); // schedule animation
expect(find.text('test'), findsOneWidget);
await tester.pump(); // begin animation
await tester.pump(const Duration(milliseconds: 4750)); // 4.75s
expect(find.text('test'), findsOneWidget);
// Enabled accessible navigation
await boilerplate(accessibleNavigation: true);
await tester.pump(const Duration(milliseconds: 4000)); // 8.75s
await tester.pump();
expect(find.text('test'), findsOneWidget);
// disable accessible navigation
await boilerplate(accessibleNavigation: false);
await tester.pumpAndSettle(const Duration(milliseconds: 5750));
expect(find.text('test'), findsNothing);
});
} }
...@@ -43,6 +43,9 @@ void main() { ...@@ -43,6 +43,9 @@ void main() {
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(ui.window.physicalSize / ui.window.devicePixelRatio));
expect(data.accessibleNavigation, false);
expect(data.invertColors, false);
expect(data.disableAnimations, false);
}); });
testWidgets('MediaQueryData.copyWith defaults to source', (WidgetTester tester) async { testWidgets('MediaQueryData.copyWith defaults to source', (WidgetTester tester) async {
...@@ -54,6 +57,9 @@ void main() { ...@@ -54,6 +57,9 @@ void main() {
expect(copied.padding, data.padding); expect(copied.padding, data.padding);
expect(copied.viewInsets, data.viewInsets); expect(copied.viewInsets, data.viewInsets);
expect(copied.alwaysUse24HourFormat, data.alwaysUse24HourFormat); expect(copied.alwaysUse24HourFormat, data.alwaysUse24HourFormat);
expect(copied.accessibleNavigation, data.accessibleNavigation);
expect(copied.invertColors, data.invertColors);
expect(copied.disableAnimations, data.disableAnimations);
}); });
testWidgets('MediaQuery.copyWith copies specified values', (WidgetTester tester) async { testWidgets('MediaQuery.copyWith copies specified values', (WidgetTester tester) async {
...@@ -65,6 +71,9 @@ void main() { ...@@ -65,6 +71,9 @@ void main() {
padding: const EdgeInsets.all(9.10938), padding: const EdgeInsets.all(9.10938),
viewInsets: const EdgeInsets.all(1.67262), viewInsets: const EdgeInsets.all(1.67262),
alwaysUse24HourFormat: true, alwaysUse24HourFormat: true,
accessibleNavigation: true,
invertColors: true,
disableAnimations: true,
); );
expect(copied.size, const Size(3.14, 2.72)); expect(copied.size, const Size(3.14, 2.72));
expect(copied.devicePixelRatio, 1.41); expect(copied.devicePixelRatio, 1.41);
...@@ -72,6 +81,9 @@ void main() { ...@@ -72,6 +81,9 @@ void main() {
expect(copied.padding, const EdgeInsets.all(9.10938)); expect(copied.padding, const EdgeInsets.all(9.10938));
expect(copied.viewInsets, const EdgeInsets.all(1.67262)); expect(copied.viewInsets, const EdgeInsets.all(1.67262));
expect(copied.alwaysUse24HourFormat, true); expect(copied.alwaysUse24HourFormat, true);
expect(copied.accessibleNavigation, true);
expect(copied.invertColors, true);
expect(copied.disableAnimations, true);
}); });
testWidgets('MediaQuery.removePadding removes specified padding', (WidgetTester tester) async { testWidgets('MediaQuery.removePadding removes specified padding', (WidgetTester tester) async {
...@@ -91,6 +103,9 @@ void main() { ...@@ -91,6 +103,9 @@ void main() {
padding: padding, padding: padding,
viewInsets: viewInsets, viewInsets: viewInsets,
alwaysUse24HourFormat: true, alwaysUse24HourFormat: true,
accessibleNavigation: true,
invertColors: true,
disableAnimations: true,
), ),
child: new Builder( child: new Builder(
builder: (BuildContext context) { builder: (BuildContext context) {
...@@ -118,6 +133,9 @@ void main() { ...@@ -118,6 +133,9 @@ void main() {
expect(unpadded.padding, EdgeInsets.zero); expect(unpadded.padding, EdgeInsets.zero);
expect(unpadded.viewInsets, viewInsets); expect(unpadded.viewInsets, viewInsets);
expect(unpadded.alwaysUse24HourFormat, true); expect(unpadded.alwaysUse24HourFormat, true);
expect(unpadded.accessibleNavigation, true);
expect(unpadded.invertColors, true);
expect(unpadded.disableAnimations, true);
}); });
testWidgets('MediaQuery.removeViewInsets removes specified viewInsets', (WidgetTester tester) async { testWidgets('MediaQuery.removeViewInsets removes specified viewInsets', (WidgetTester tester) async {
...@@ -137,6 +155,9 @@ void main() { ...@@ -137,6 +155,9 @@ void main() {
padding: padding, padding: padding,
viewInsets: viewInsets, viewInsets: viewInsets,
alwaysUse24HourFormat: true, alwaysUse24HourFormat: true,
accessibleNavigation: true,
invertColors: true,
disableAnimations: true,
), ),
child: new Builder( child: new Builder(
builder: (BuildContext context) { builder: (BuildContext context) {
...@@ -164,6 +185,9 @@ void main() { ...@@ -164,6 +185,9 @@ void main() {
expect(unpadded.padding, padding); expect(unpadded.padding, padding);
expect(unpadded.viewInsets, EdgeInsets.zero); expect(unpadded.viewInsets, EdgeInsets.zero);
expect(unpadded.alwaysUse24HourFormat, true); expect(unpadded.alwaysUse24HourFormat, true);
expect(unpadded.accessibleNavigation, true);
expect(unpadded.invertColors, true);
expect(unpadded.disableAnimations, true);
}); });
testWidgets('MediaQuery.textScaleFactorOf', (WidgetTester tester) async { testWidgets('MediaQuery.textScaleFactorOf', (WidgetTester tester) async {
......
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