diff --git a/packages/flutter/lib/src/material/app.dart b/packages/flutter/lib/src/material/app.dart index 954b226067..ed376d6122 100644 --- a/packages/flutter/lib/src/material/app.dart +++ b/packages/flutter/lib/src/material/app.dart @@ -2,6 +2,8 @@ // 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/cupertino.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; @@ -96,6 +98,7 @@ class MaterialApp extends StatefulWidget { this.onGenerateTitle, this.color, this.theme, + this.darkTheme, this.locale, this.localizationsDelegates, this.localeListResolutionCallback, @@ -163,9 +166,44 @@ class MaterialApp extends StatefulWidget { /// This value is passed unmodified to [WidgetsApp.onGenerateTitle]. final GenerateAppTitle onGenerateTitle; - /// The colors to use for the application's widgets. + /// Default visual properties, like colors fonts and shapes, for this app's + /// material widgets. + /// + /// A second [darkTheme] [ThemeData] value, which is used when the underlying + /// platform requests a "dark mode" UI, can also be specified. + /// + /// The default value of this property is the value of [ThemeData.light()]. + /// + /// See also: + /// + /// * [MediaQueryData.platformBrightness], which indicates the platform's + /// desired brightness and is used to automatically toggle between [theme] + /// and [darkTheme] in [MaterialApp]. + /// * [ThemeData.brightness], which indicates the [Brightness] of a theme's + /// colors. final ThemeData theme; + /// The [ThemeData] to use when the platform specifically requests a dark + /// themed UI. + /// + /// Host platforms such as Android Pie can request a system-wide "dark mode" + /// when entering battery saver mode. + /// + /// When the host platform requests a [Brightness.dark] mode, you may want to + /// supply a [ThemeData.brightness] that's also [Brightness.dark]. + /// + /// Uses [theme] instead when null. Defaults to the value of + /// [ThemeData.light()] when both [darkTheme] and [theme] are null. + /// + /// See also: + /// + /// * [MediaQueryData.platformBrightness], which indicates the platform's + /// desired brightness and is used to automatically toggle between [theme] + /// and [darkTheme] in [MaterialApp]. + /// * [ThemeData.brightness], which is typically set to the value of + /// [MediaQueryData.platformBrightness]. + final ThemeData darkTheme; + /// {@macro flutter.widgets.widgetsApp.color} final Color color; @@ -403,45 +441,80 @@ class _MaterialAppState extends State { @override Widget build(BuildContext context) { - final ThemeData theme = widget.theme ?? ThemeData.fallback(); - Widget result = AnimatedTheme( - data: theme, - isMaterialAppTheme: true, - child: WidgetsApp( - key: GlobalObjectKey(this), - navigatorKey: widget.navigatorKey, - navigatorObservers: _navigatorObservers, + Widget result = WidgetsApp( + key: GlobalObjectKey(this), + navigatorKey: widget.navigatorKey, + navigatorObservers: _navigatorObservers, pageRouteBuilder: (RouteSettings settings, WidgetBuilder builder) => - MaterialPageRoute(settings: settings, builder: builder), - home: widget.home, - routes: widget.routes, - initialRoute: widget.initialRoute, - onGenerateRoute: widget.onGenerateRoute, - onUnknownRoute: widget.onUnknownRoute, - builder: widget.builder, - title: widget.title, - onGenerateTitle: widget.onGenerateTitle, - textStyle: _errorTextStyle, - // blue is the primary color of the default theme - color: widget.color ?? theme?.primaryColor ?? Colors.blue, - locale: widget.locale, - localizationsDelegates: _localizationsDelegates, - localeResolutionCallback: widget.localeResolutionCallback, - localeListResolutionCallback: widget.localeListResolutionCallback, - supportedLocales: widget.supportedLocales, - showPerformanceOverlay: widget.showPerformanceOverlay, - checkerboardRasterCacheImages: widget.checkerboardRasterCacheImages, - checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers, - showSemanticsDebugger: widget.showSemanticsDebugger, - debugShowCheckedModeBanner: widget.debugShowCheckedModeBanner, - inspectorSelectButtonBuilder: (BuildContext context, VoidCallback onPressed) { - return FloatingActionButton( - child: const Icon(Icons.search), - onPressed: onPressed, - mini: true, - ); - }, - ), + MaterialPageRoute(settings: settings, builder: builder), + home: widget.home, + routes: widget.routes, + initialRoute: widget.initialRoute, + onGenerateRoute: widget.onGenerateRoute, + onUnknownRoute: widget.onUnknownRoute, + builder: (BuildContext context, Widget child) { + // Use a light theme, dark theme, or fallback theme. + ThemeData theme; + final ui.Brightness platformBrightness = MediaQuery.platformBrightnessOf(context); + if (platformBrightness == ui.Brightness.dark && widget.darkTheme != null) { + theme = widget.darkTheme; + } else if (widget.theme != null) { + theme = widget.theme; + } else { + theme = ThemeData.fallback(); + } + + return AnimatedTheme( + data: theme, + isMaterialAppTheme: true, + child: widget.builder != null + ? Builder( + builder: (BuildContext context) { + // Why are we surrounding a builder with a builder? + // + // The widget.builder may contain code that invokes + // Theme.of(), which should return the theme we selected + // above in AnimatedTheme. However, if we invoke + // widget.builder() directly as the child of AnimatedTheme + // then there is no Context separating them, and the + // widget.builder() will not find the theme. Therefore, we + // surround widget.builder with yet another builder so that + // a context separates them and Theme.of() correctly + // resolves to the theme we passed to AnimatedTheme. + return widget.builder(context, child); + }, + ) + : child, + ); + }, + title: widget.title, + onGenerateTitle: widget.onGenerateTitle, + textStyle: _errorTextStyle, + // The color property is always pulled from the light theme, even if dark + // mode is activated. This was done to simplify the technical details + // of switching themes and it was deemed acceptable because this color + // property is only used on old Android OSes to color the app bar in + // Android's switcher UI. + // + // blue is the primary color of the default theme + color: widget.color ?? widget.theme?.primaryColor ?? Colors.blue, + locale: widget.locale, + localizationsDelegates: _localizationsDelegates, + localeResolutionCallback: widget.localeResolutionCallback, + localeListResolutionCallback: widget.localeListResolutionCallback, + supportedLocales: widget.supportedLocales, + showPerformanceOverlay: widget.showPerformanceOverlay, + checkerboardRasterCacheImages: widget.checkerboardRasterCacheImages, + checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers, + showSemanticsDebugger: widget.showSemanticsDebugger, + debugShowCheckedModeBanner: widget.debugShowCheckedModeBanner, + inspectorSelectButtonBuilder: (BuildContext context, VoidCallback onPressed) { + return FloatingActionButton( + child: const Icon(Icons.search), + onPressed: onPressed, + mini: true, + ); + }, ); assert(() { diff --git a/packages/flutter/lib/src/rendering/binding.dart b/packages/flutter/lib/src/rendering/binding.dart index 8c2b934e79..b2fd948bd4 100644 --- a/packages/flutter/lib/src/rendering/binding.dart +++ b/packages/flutter/lib/src/rendering/binding.dart @@ -33,6 +33,7 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture window ..onMetricsChanged = handleMetricsChanged ..onTextScaleFactorChanged = handleTextScaleFactorChanged + ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged ..onSemanticsAction = _handleSemanticsAction; initRenderView(); @@ -168,6 +169,38 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture @protected void handleTextScaleFactorChanged() { } + /// {@template on_platform_brightness_change} + /// Called when the platform brightness changes. + /// + /// The current platform brightness can be queried either from a Flutter + /// binding, or from a [MediaQuery] widget. + /// + /// ## Sample Code + /// + /// Querying [Window.platformBrightness]: + /// + /// ```dart + /// final Brightness brightness = WidgetsBinding.instance.window.platformBrightness; + /// ``` + /// + /// Querying [MediaQuery] directly: + /// + /// ```dart + /// final Brightness brightness = MediaQuery.platformBrightnessOf(context); + /// ``` + /// + /// Querying [MediaQueryData]: + /// + /// ```dart + /// final MediaQueryData mediaQueryData = MediaQuery.of(context); + /// final Brightness brightness = mediaQueryData.platformBrightness; + /// ``` + /// + /// See [Window.onPlatformBrightnessChanged]. + /// {@endtemplate} + @protected + void handlePlatformBrightnessChanged() { } + /// Returns a [ViewConfiguration] configured for the [RenderView] based on the /// current environment. /// diff --git a/packages/flutter/lib/src/widgets/app.dart b/packages/flutter/lib/src/widgets/app.dart index 7934569aa3..32b486f5e7 100644 --- a/packages/flutter/lib/src/widgets/app.dart +++ b/packages/flutter/lib/src/widgets/app.dart @@ -1020,6 +1020,15 @@ class _WidgetsAppState extends State implements WidgetsBindingObserv }); } + // RENDERING + @override + void didChangePlatformBrightness() { + setState(() { + // The platformBrightness 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. + }); + } // BUILDER diff --git a/packages/flutter/lib/src/widgets/binding.dart b/packages/flutter/lib/src/widgets/binding.dart index 76b166ee98..bbe9944569 100644 --- a/packages/flutter/lib/src/widgets/binding.dart +++ b/packages/flutter/lib/src/widgets/binding.dart @@ -213,6 +213,9 @@ abstract class WidgetsBindingObserver { /// boilerplate. void didChangeTextScaleFactor() { } + /// {@macro on_platform_brightness_change} + void didChangePlatformBrightness() { } + /// Called when the system tells the app that the user's locale has /// changed. For example, if the user changes the system language /// settings. @@ -408,6 +411,13 @@ mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererB observer.didChangeTextScaleFactor(); } + @override + void handlePlatformBrightnessChanged() { + super.handlePlatformBrightnessChanged(); + for (WidgetsBindingObserver observer in _observers) + observer.didChangePlatformBrightness(); + } + @override void handleAccessibilityFeaturesChanged() { super.handleAccessibilityFeaturesChanged(); diff --git a/packages/flutter/lib/src/widgets/media_query.dart b/packages/flutter/lib/src/widgets/media_query.dart index 991f5b0d9c..7079c08b63 100644 --- a/packages/flutter/lib/src/widgets/media_query.dart +++ b/packages/flutter/lib/src/widgets/media_query.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:ui' as ui; +import 'dart:ui' show Brightness; import 'package:flutter/foundation.dart'; @@ -59,6 +60,7 @@ class MediaQueryData { this.size = Size.zero, this.devicePixelRatio = 1.0, this.textScaleFactor = 1.0, + this.platformBrightness = Brightness.light, this.padding = EdgeInsets.zero, this.viewInsets = EdgeInsets.zero, this.alwaysUse24HourFormat = false, @@ -78,6 +80,7 @@ class MediaQueryData { : size = window.physicalSize / window.devicePixelRatio, devicePixelRatio = window.devicePixelRatio, textScaleFactor = window.textScaleFactor, + platformBrightness = window.platformBrightness, padding = EdgeInsets.fromWindowPadding(window.padding, window.devicePixelRatio), viewInsets = EdgeInsets.fromWindowPadding(window.viewInsets, window.devicePixelRatio), accessibleNavigation = window.accessibilityFeatures.accessibleNavigation, @@ -110,6 +113,15 @@ class MediaQueryData { /// textScaleFactor defined for a [BuildContext]. final double textScaleFactor; + /// The current brightness mode of the host platform. + /// + /// For example, starting in Android Pie, battery saver mode asks all apps to + /// render in a "dark mode". + /// + /// Not all platforms necessarily support a concept of brightness mode. Those + /// platforms will report [Brightness.light] in this property. + final Brightness platformBrightness; + /// The parts of the display that are completely obscured by system UI, /// typically by the device's keyboard. /// @@ -204,6 +216,7 @@ class MediaQueryData { Size size, double devicePixelRatio, double textScaleFactor, + Brightness platformBrightness, EdgeInsets padding, EdgeInsets viewInsets, bool alwaysUse24HourFormat, @@ -216,6 +229,7 @@ class MediaQueryData { size: size ?? this.size, devicePixelRatio: devicePixelRatio ?? this.devicePixelRatio, textScaleFactor: textScaleFactor ?? this.textScaleFactor, + platformBrightness: platformBrightness ?? this.platformBrightness, padding: padding ?? this.padding, viewInsets: viewInsets ?? this.viewInsets, alwaysUse24HourFormat: alwaysUse24HourFormat ?? this.alwaysUse24HourFormat, @@ -252,6 +266,7 @@ class MediaQueryData { size: size, devicePixelRatio: devicePixelRatio, textScaleFactor: textScaleFactor, + platformBrightness: platformBrightness, padding: padding.copyWith( left: removeLeft ? 0.0 : null, top: removeTop ? 0.0 : null, @@ -291,6 +306,7 @@ class MediaQueryData { size: size, devicePixelRatio: devicePixelRatio, textScaleFactor: textScaleFactor, + platformBrightness: platformBrightness, padding: padding, viewInsets: viewInsets.copyWith( left: removeLeft ? 0.0 : null, @@ -314,6 +330,7 @@ class MediaQueryData { return typedOther.size == size && typedOther.devicePixelRatio == devicePixelRatio && typedOther.textScaleFactor == textScaleFactor + && typedOther.platformBrightness == platformBrightness && typedOther.padding == padding && typedOther.viewInsets == viewInsets && typedOther.alwaysUse24HourFormat == alwaysUse24HourFormat @@ -329,6 +346,7 @@ class MediaQueryData { size, devicePixelRatio, textScaleFactor, + platformBrightness, padding, viewInsets, alwaysUse24HourFormat, @@ -345,6 +363,7 @@ class MediaQueryData { 'size: $size, ' 'devicePixelRatio: ${devicePixelRatio.toStringAsFixed(1)}, ' 'textScaleFactor: ${textScaleFactor.toStringAsFixed(1)}, ' + 'platformBrightness: $platformBrightness, ' 'padding: $padding, ' 'viewInsets: $viewInsets, ' 'alwaysUse24HourFormat: $alwaysUse24HourFormat, ' @@ -523,6 +542,15 @@ class MediaQuery extends InheritedWidget { return MediaQuery.of(context, nullOk: true)?.textScaleFactor ?? 1.0; } + /// Returns platformBrightness for the nearest MediaQuery ancestor or + /// [Brightness.light], if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// any property of the ancestor [MediaQuery] changes. + static Brightness platformBrightnessOf(BuildContext context) { + return MediaQuery.of(context, nullOk: true)?.platformBrightness ?? Brightness.light; + } + /// Returns the boldText accessibility setting for the nearest MediaQuery /// ancestor, or false if no such ancestor exists. static bool boldTextOverride(BuildContext context) { diff --git a/packages/flutter/test/material/app_test.dart b/packages/flutter/test/material/app_test.dart index 36c2233372..fa68c30a21 100644 --- a/packages/flutter/test/material/app_test.dart +++ b/packages/flutter/test/material/app_test.dart @@ -435,4 +435,165 @@ void main() { // Default Cupertino US "select all" text. expect(find.text('Select All'), findsOneWidget); }); + + testWidgets('MaterialApp uses regular theme when platformBrightness is light', (WidgetTester tester) async { + // Mock the Window to explicitly report a light platformBrightness. + final TestWidgetsFlutterBinding binding = tester.binding; + binding.window.platformBrightnessTestValue = Brightness.light; + + ThemeData appliedTheme; + + await tester.pumpWidget( + MaterialApp( + theme: ThemeData( + brightness: Brightness.light + ), + darkTheme: ThemeData( + brightness: Brightness.dark, + ), + home: Builder( + builder: (BuildContext context) { + appliedTheme = Theme.of(context); + return const SizedBox(); + }, + ), + ), + ); + + expect(appliedTheme.brightness, Brightness.light); + }); + + testWidgets('MaterialApp uses light theme when platformBrightness is dark but no dark theme is provided', (WidgetTester tester) async { + // Mock the Window to explicitly report a dark platformBrightness. + final TestWidgetsFlutterBinding binding = tester.binding; + binding.window.platformBrightnessTestValue = Brightness.dark; + + ThemeData appliedTheme; + + await tester.pumpWidget( + MaterialApp( + theme: ThemeData( + brightness: Brightness.light + ), + home: Builder( + builder: (BuildContext context) { + appliedTheme = Theme.of(context); + return const SizedBox(); + }, + ), + ), + ); + + expect(appliedTheme.brightness, Brightness.light); + }); + + testWidgets('MaterialApp uses fallback light theme when platformBrightness is dark but no theme is provided at all', (WidgetTester tester) async { + // Mock the Window to explicitly report a dark platformBrightness. + final TestWidgetsFlutterBinding binding = tester.binding; + binding.window.platformBrightnessTestValue = Brightness.dark; + + ThemeData appliedTheme; + + await tester.pumpWidget( + MaterialApp( + home: Builder( + builder: (BuildContext context) { + appliedTheme = Theme.of(context); + return const SizedBox(); + }, + ), + ), + ); + + expect(appliedTheme.brightness, Brightness.light); + }); + + testWidgets('MaterialApp uses fallback light theme when platformBrightness is light and a dark theme is provided', (WidgetTester tester) async { + // Mock the Window to explicitly report a dark platformBrightness. + final TestWidgetsFlutterBinding binding = tester.binding; + binding.window.platformBrightnessTestValue = Brightness.light; + + ThemeData appliedTheme; + + await tester.pumpWidget( + MaterialApp( + darkTheme: ThemeData( + brightness: Brightness.dark, + ), + home: Builder( + builder: (BuildContext context) { + appliedTheme = Theme.of(context); + return const SizedBox(); + }, + ), + ), + ); + + expect(appliedTheme.brightness, Brightness.light); + }); + + testWidgets('MaterialApp uses dark theme when platformBrightness is dark', (WidgetTester tester) async { + // Mock the Window to explicitly report a dark platformBrightness. + final TestWidgetsFlutterBinding binding = tester.binding; + binding.window.platformBrightnessTestValue = Brightness.dark; + + ThemeData appliedTheme; + + await tester.pumpWidget( + MaterialApp( + theme: ThemeData( + brightness: Brightness.light + ), + darkTheme: ThemeData( + brightness: Brightness.dark, + ), + home: Builder( + builder: (BuildContext context) { + appliedTheme = Theme.of(context); + return const SizedBox(); + }, + ), + ), + ); + + expect(appliedTheme.brightness, Brightness.dark); + }); + + testWidgets('MaterialApp switches themes when the Window platformBrightness changes.', (WidgetTester tester) async { + // Mock the Window to explicitly report a light platformBrightness. + final TestWidgetsFlutterBinding binding = tester.binding; + binding.window.platformBrightnessTestValue = Brightness.light; + + ThemeData themeBeforeBrightnessChange; + ThemeData themeAfterBrightnessChange; + + await tester.pumpWidget( + MaterialApp( + theme: ThemeData( + brightness: Brightness.light + ), + darkTheme: ThemeData( + brightness: Brightness.dark, + ), + home: Builder( + builder: (BuildContext context) { + if (themeBeforeBrightnessChange == null) { + themeBeforeBrightnessChange = Theme.of(context); + } else { + themeAfterBrightnessChange = Theme.of(context); + } + return const SizedBox(); + }, + ), + ), + ); + + // Switch the platformBrightness from light to dark and pump the widget tree + // to process changes. + binding.window.platformBrightnessTestValue = Brightness.dark; + await tester.pumpAndSettle(); + + expect(themeBeforeBrightnessChange.brightness, Brightness.light); + expect(themeAfterBrightnessChange.brightness, Brightness.dark); + }); } diff --git a/packages/flutter/test/widgets/media_query_test.dart b/packages/flutter/test/widgets/media_query_test.dart index 9e0f97df34..c06d5e2ab1 100644 --- a/packages/flutter/test/widgets/media_query_test.dart +++ b/packages/flutter/test/widgets/media_query_test.dart @@ -2,6 +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' show Brightness; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/widgets.dart'; @@ -45,6 +46,7 @@ void main() { expect(data.invertColors, false); expect(data.disableAnimations, false); expect(data.boldText, false); + expect(data.platformBrightness, Brightness.light); }); testWidgets('MediaQueryData.copyWith defaults to source', (WidgetTester tester) async { @@ -60,6 +62,7 @@ void main() { expect(copied.invertColors, data.invertColors); expect(copied.disableAnimations, data.disableAnimations); expect(copied.boldText, data.boldText); + expect(copied.platformBrightness, data.platformBrightness); }); testWidgets('MediaQuery.copyWith copies specified values', (WidgetTester tester) async { @@ -75,6 +78,7 @@ void main() { invertColors: true, disableAnimations: true, boldText: true, + platformBrightness: Brightness.dark, ); expect(copied.size, const Size(3.14, 2.72)); expect(copied.devicePixelRatio, 1.41); @@ -86,6 +90,7 @@ void main() { expect(copied.invertColors, true); expect(copied.disableAnimations, true); expect(copied.boldText, true); + expect(copied.platformBrightness, Brightness.dark); }); testWidgets('MediaQuery.removePadding removes specified padding', (WidgetTester tester) async { @@ -223,6 +228,33 @@ void main() { expect(insideTextScaleFactor, 4.0); }); + testWidgets('MediaQuery.platformBrightnessOf', (WidgetTester tester) async { + Brightness outsideBrightness; + Brightness insideBrightness; + + await tester.pumpWidget( + Builder( + builder: (BuildContext context) { + outsideBrightness = MediaQuery.platformBrightnessOf(context); + return MediaQuery( + data: const MediaQueryData( + platformBrightness: Brightness.dark, + ), + child: Builder( + builder: (BuildContext context) { + insideBrightness = MediaQuery.platformBrightnessOf(context); + return Container(); + }, + ), + ); + }, + ), + ); + + expect(outsideBrightness, Brightness.light); + expect(insideBrightness, Brightness.dark); + }); + testWidgets('MediaQuery.boldTextOverride', (WidgetTester tester) async { bool outsideBoldTextOverride; bool insideBoldTextOverride; diff --git a/packages/flutter_test/lib/src/window.dart b/packages/flutter_test/lib/src/window.dart index 587c538e9e..a5983fe639 100644 --- a/packages/flutter_test/lib/src/window.dart +++ b/packages/flutter_test/lib/src/window.dart @@ -59,10 +59,12 @@ class TestWindow implements Window { /// Hides the real device pixel ratio and reports the given [devicePixelRatio] instead. set devicePixelRatioTestValue(double devicePixelRatio) { _devicePixelRatio = devicePixelRatio; + onMetricsChanged(); } /// Deletes any existing test device pixel ratio and returns to using the real device pixel ratio. void clearDevicePixelRatioTestValue() { _devicePixelRatio = null; + onMetricsChanged(); } @override @@ -71,10 +73,12 @@ class TestWindow implements Window { /// Hides the real physical size and reports the given [physicalSizeTestValue] instead. set physicalSizeTestValue (Size physicalSizeTestValue) { _physicalSizeTestValue = physicalSizeTestValue; + onMetricsChanged(); } /// Deletes any existing test physical size and returns to using the real physical size. void clearPhysicalSizeTestValue() { _physicalSizeTestValue = null; + onMetricsChanged(); } @override @@ -83,10 +87,12 @@ class TestWindow implements Window { /// Hides the real view insets and reports the given [viewInsetsTestValue] instead. set viewInsetsTestValue(WindowPadding viewInsetsTestValue) { _viewInsetsTestValue = viewInsetsTestValue; + onMetricsChanged(); } /// Deletes any existing test view insets and returns to using the real view insets. void clearViewInsetsTestValue() { _viewInsetsTestValue = null; + onMetricsChanged(); } @override @@ -95,10 +101,12 @@ class TestWindow implements Window { /// Hides the real padding and reports the given [paddingTestValue] instead. set paddingTestValue(WindowPadding paddingTestValue) { _paddingTestValue = paddingTestValue; + onMetricsChanged(); } /// Deletes any existing test padding and returns to using the real padding. void clearPaddingTestValue() { _paddingTestValue = null; + onMetricsChanged(); } @override @@ -114,10 +122,12 @@ class TestWindow implements Window { /// Hides the real locale and reports the given [localeTestValue] instead. set localeTestValue(Locale localeTestValue) { _localeTestValue = localeTestValue; + onLocaleChanged(); } /// Deletes any existing test locale and returns to using the real locale. void clearLocaleTestValue() { _localeTestValue = null; + onLocaleChanged(); } @override @@ -126,10 +136,12 @@ class TestWindow implements Window { /// Hides the real locales and reports the given [localesTestValue] instead. set localesTestValue(List localesTestValue) { _localesTestValue = localesTestValue; + onLocaleChanged(); } /// Deletes any existing test locales and returns to using the real locales. void clearLocalesTestValue() { _localesTestValue = null; + onLocaleChanged(); } @override @@ -145,10 +157,12 @@ class TestWindow implements Window { /// Hides the real text scale factor and reports the given [textScaleFactorTestValue] instead. set textScaleFactorTestValue(double textScaleFactorTestValue) { _textScaleFactorTestValue = textScaleFactorTestValue; + onTextScaleFactorChanged(); } /// Deletes any existing test text scale factor and returns to using the real text scale factor. void clearTextScaleFactorTestValue() { _textScaleFactorTestValue = null; + onTextScaleFactorChanged(); } @override @@ -158,15 +172,17 @@ class TestWindow implements Window { VoidCallback get onPlatformBrightnessChanged => _window.onPlatformBrightnessChanged; @override set onPlatformBrightnessChanged(VoidCallback callback) { - _window.onPlatformBrightnessChanged =callback; + _window.onPlatformBrightnessChanged = callback; } /// Hides the real text scale factor and reports the given [platformBrightnessTestValue] instead. set platformBrightnessTestValue(Brightness platformBrightnessTestValue) { _platformBrightnessTestValue = platformBrightnessTestValue; + onPlatformBrightnessChanged(); } /// Deletes any existing test platform brightness and returns to using the real platform brightness. void clearPlatformBrightnessTestValue() { _platformBrightnessTestValue = null; + onPlatformBrightnessChanged(); } @override @@ -237,10 +253,12 @@ class TestWindow implements Window { /// Hides the real semantics enabled and reports the given [semanticsEnabledTestValue] instead. set semanticsEnabledTestValue(bool semanticsEnabledTestValue) { _semanticsEnabledTestValue = semanticsEnabledTestValue; + onSemanticsEnabledChanged(); } /// Deletes any existing test semantics enabled and returns to using the real semantics enabled. void clearSemanticsEnabledTestValue() { _semanticsEnabledTestValue = null; + onSemanticsEnabledChanged(); } @override @@ -263,10 +281,12 @@ class TestWindow implements Window { /// Hides the real accessibility features and reports the given [accessibilityFeaturesTestValue] instead. set accessibilityFeaturesTestValue(AccessibilityFeatures accessibilityFeaturesTestValue) { _accessibilityFeaturesTestValue = accessibilityFeaturesTestValue; + onAccessibilityFeaturesChanged(); } /// Deletes any existing test accessibility features and returns to using the real accessibility features. void clearAccessibilityFeaturesTestValue() { _accessibilityFeaturesTestValue = null; + onAccessibilityFeaturesChanged(); } @override diff --git a/packages/flutter_test/test/window_test.dart b/packages/flutter_test/test/window_test.dart index 72810cdc56..0d603d1ed9 100644 --- a/packages/flutter_test/test/window_test.dart +++ b/packages/flutter_test/test/window_test.dart @@ -136,20 +136,6 @@ void main() { ); }); - testWidgets('TestWindow can fake semantics enabled', (WidgetTester tester) async { - verifyThatTestWindowCanFakeProperty( - 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( tester: tester,