From 8d9021f22a518f02edde0dabc96cdcb9c4eb6473 Mon Sep 17 00:00:00 2001 From: maRci002 Date: Fri, 24 Mar 2023 16:33:39 +0100 Subject: [PATCH 1/7] web change status bar color --- .../lib/src/engine/platform_dispatcher.dart | 8 ++- lib/web_ui/lib/src/engine/util.dart | 19 ++++--- .../test/engine/platform_dispatcher_test.dart | 20 +++++++ .../test/ui/system_ui_overlay_style_test.dart | 53 +++++++++++++++++++ 4 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 lib/web_ui/test/ui/system_ui_overlay_style_test.dart diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index 0fcbea1600890..d6761ef7a0122 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -518,9 +518,13 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { final Map arguments = decoded.arguments as Map; // TODO(ferhat): Find more appropriate defaults? Or noop when values are null? final String label = arguments['label'] as String? ?? ''; - final int primaryColor = arguments['primaryColor'] as int? ?? 0xFF000000; domDocument.title = label; - setThemeColor(ui.Color(primaryColor)); + replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); + return; + case 'SystemChrome.setSystemUIOverlayStyle': + final Map arguments = decoded.arguments as Map; + final int? statusBarColor = arguments['statusBarColor'] as int?; + setThemeColor(statusBarColor == null ? null : ui.Color(statusBarColor)); replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; case 'SystemChrome.setPreferredOrientations': diff --git a/lib/web_ui/lib/src/engine/util.dart b/lib/web_ui/lib/src/engine/util.dart index 9400936386b7a..6201ee112b929 100644 --- a/lib/web_ui/lib/src/engine/util.dart +++ b/lib/web_ui/lib/src/engine/util.dart @@ -678,16 +678,21 @@ void setClipPath(DomElement element, String? value) { } } -void setThemeColor(ui.Color color) { +void setThemeColor(ui.Color? color) { DomHTMLMetaElement? theme = domDocument.querySelector('#flutterweb-theme') as DomHTMLMetaElement?; - if (theme == null) { - theme = createDomHTMLMetaElement() - ..id = 'flutterweb-theme' - ..name = 'theme-color'; - domDocument.head!.append(theme); + + if (color != null) { + if (theme == null) { + theme = createDomHTMLMetaElement() + ..id = 'flutterweb-theme' + ..name = 'theme-color'; + domDocument.head!.append(theme); + } + theme.content = colorToCssString(color)!; + } else if (theme != null) { + theme.remove(); } - theme.content = colorToCssString(color)!; } bool? _ellipseFeatureDetected; diff --git a/lib/web_ui/test/engine/platform_dispatcher_test.dart b/lib/web_ui/test/engine/platform_dispatcher_test.dart index 1b19330b3870d..4cb9898dabfa1 100644 --- a/lib/web_ui/test/engine/platform_dispatcher_test.dart +++ b/lib/web_ui/test/engine/platform_dispatcher_test.dart @@ -72,6 +72,26 @@ void testMain() { ); }); + test('responds to flutter/platform SystemChrome.setSystemUIOverlayStyle', + () async { + const MethodCodec codec = JSONMethodCodec(); + final Completer completer = Completer(); + ui.PlatformDispatcher.instance.sendPlatformMessage( + 'flutter/platform', + codec.encodeMethodCall(const MethodCall( + 'SystemChrome.setSystemUIOverlayStyle', + )), + completer.complete, + ); + + final ByteData? response = await completer.future; + expect(response, isNotNull); + expect( + codec.decodeEnvelope(response!), + true, + ); + }); + test('responds to flutter/contextmenu enable', () async { const MethodCodec codec = JSONMethodCodec(); final Completer completer = Completer(); diff --git a/lib/web_ui/test/ui/system_ui_overlay_style_test.dart b/lib/web_ui/test/ui/system_ui_overlay_style_test.dart new file mode 100644 index 0000000000000..36c5fab22c61d --- /dev/null +++ b/lib/web_ui/test/ui/system_ui_overlay_style_test.dart @@ -0,0 +1,53 @@ +// Copyright 2013 The Flutter 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 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'utils.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + setUpUiTest(); + + const MethodCodec codec = JSONMethodCodec(); + + void sendSetSystemUIOverlayStyle({ui.Color? statusBarColor}) { + ui.window.sendPlatformMessage( + 'flutter/platform', + codec.encodeMethodCall( + MethodCall('SystemChrome.setSystemUIOverlayStyle', { + 'statusBarColor': statusBarColor?.value, + })), + null, + ); + } + + String? getCssThemeColor() { + final DomHTMLMetaElement? theme = + domDocument.querySelector('#flutterweb-theme') as DomHTMLMetaElement?; + return theme?.content; + } + + group('SystemUIOverlayStyle', () { + test('theme color is set / removed by platform message', () { + // Run the unit test without emulating Flutter tester environment. + ui.debugEmulateFlutterTesterEnvironment = false; + + expect(getCssThemeColor(), null); + + const ui.Color statusBarColor = ui.Color(0xFFF44336); + sendSetSystemUIOverlayStyle(statusBarColor: statusBarColor); + expect(getCssThemeColor(), colorToCssString(statusBarColor)); + + sendSetSystemUIOverlayStyle(); + expect(getCssThemeColor(), null); + }); + }); +} From 6dbd7874c6f530c1046222897c87e85d437255f0 Mon Sep 17 00:00:00 2001 From: maRci002 Date: Fri, 24 Mar 2023 18:00:47 +0100 Subject: [PATCH 2/7] add arguments for SystemChrome.setSystemUIOverlayStyle message --- lib/web_ui/test/engine/platform_dispatcher_test.dart | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/web_ui/test/engine/platform_dispatcher_test.dart b/lib/web_ui/test/engine/platform_dispatcher_test.dart index 4cb9898dabfa1..7cecb112425c8 100644 --- a/lib/web_ui/test/engine/platform_dispatcher_test.dart +++ b/lib/web_ui/test/engine/platform_dispatcher_test.dart @@ -80,6 +80,7 @@ void testMain() { 'flutter/platform', codec.encodeMethodCall(const MethodCall( 'SystemChrome.setSystemUIOverlayStyle', + {}, )), completer.complete, ); @@ -164,7 +165,8 @@ void testMain() { () async { final DomElement root = domDocument.documentElement!; final String oldFontSize = root.style.fontSize; - final ui.VoidCallback? oldCallback = ui.PlatformDispatcher.instance.onTextScaleFactorChanged; + final ui.VoidCallback? oldCallback = + ui.PlatformDispatcher.instance.onTextScaleFactorChanged; addTearDown(() { root.style.fontSize = oldFontSize; @@ -182,7 +184,8 @@ void testMain() { await Future.delayed(Duration.zero); expect(root.style.fontSize, '20px'); expect(isCalled, isTrue); - expect(ui.PlatformDispatcher.instance.textScaleFactor, findBrowserTextScaleFactor()); + expect(ui.PlatformDispatcher.instance.textScaleFactor, + findBrowserTextScaleFactor()); isCalled = false; @@ -190,7 +193,8 @@ void testMain() { await Future.delayed(Duration.zero); expect(root.style.fontSize, '16px'); expect(isCalled, isTrue); - expect(ui.PlatformDispatcher.instance.textScaleFactor, findBrowserTextScaleFactor()); + expect(ui.PlatformDispatcher.instance.textScaleFactor, + findBrowserTextScaleFactor()); }); }); } @@ -203,7 +207,6 @@ class MockHighContrastSupport implements HighContrastSupport { @override bool get isHighContrastEnabled => isEnabled; - void invokeListeners(bool val) { for (final HighContrastListener listener in _listeners) { listener(val); From 77176c8633aa29a045ab42d00260d6751604e8f6 Mon Sep 17 00:00:00 2001 From: maRci002 Date: Tue, 11 Apr 2023 13:13:05 +0200 Subject: [PATCH 3/7] Update based on conversation --- .../lib/src/engine/platform_dispatcher.dart | 16 ++++++++++++++-- lib/web_ui/lib/src/engine/util.dart | 4 ++-- .../platform_dispatcher_test.dart | 2 +- .../system_ui_overlay_style_test.dart | 2 +- 4 files changed, 18 insertions(+), 6 deletions(-) rename lib/web_ui/test/{canvaskit => engine/platform_dispatcher}/platform_dispatcher_test.dart (96%) rename lib/web_ui/test/{ui => engine/platform_dispatcher}/system_ui_overlay_style_test.dart (98%) diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index f250e2e4a5a79..04d128e282cd1 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -514,14 +514,26 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; case 'SystemChrome.setApplicationSwitcherDescription': - final Map arguments = decoded.arguments as Map; + final Map arguments = decoded.arguments as Map; // TODO(ferhat): Find more appropriate defaults? Or noop when values are null? final String label = arguments['label'] as String? ?? ''; + final int? primaryColor = arguments['primaryColor'] as int?; + if (primaryColor != null) { + domWindow.console.warn( + 'The `primaryColor` in `SystemChrome.setApplicationSwitcherDescription` ' + 'is deprecated on the web.\n' + 'Use `SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(systemNavigationBarColor: ...)) ' + 'instead.\n' + 'If you did not directly call the `SystemChrome.setSystemUIOverlayStyle` ' + 'method, you can ignore this message. It is most likely ' + 'triggered by the `Title` widget, which is used by `MaterialApp` / `CupertinoApp` / `WidgetsApp`.\n' + 'See: https://github.com/flutter/flutter/issues/123365'); + } domDocument.title = label; replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; case 'SystemChrome.setSystemUIOverlayStyle': - final Map arguments = decoded.arguments as Map; + final Map arguments = decoded.arguments as Map; final int? statusBarColor = arguments['statusBarColor'] as int?; setThemeColor(statusBarColor == null ? null : ui.Color(statusBarColor)); replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); diff --git a/lib/web_ui/lib/src/engine/util.dart b/lib/web_ui/lib/src/engine/util.dart index 296fd2ed82474..0f81e9dcaea79 100644 --- a/lib/web_ui/lib/src/engine/util.dart +++ b/lib/web_ui/lib/src/engine/util.dart @@ -691,8 +691,8 @@ void setThemeColor(ui.Color? color) { domDocument.head!.append(theme); } theme.content = colorToCssString(color)!; - } else if (theme != null) { - theme.remove(); + } else { + theme?.remove(); } } diff --git a/lib/web_ui/test/canvaskit/platform_dispatcher_test.dart b/lib/web_ui/test/engine/platform_dispatcher/platform_dispatcher_test.dart similarity index 96% rename from lib/web_ui/test/canvaskit/platform_dispatcher_test.dart rename to lib/web_ui/test/engine/platform_dispatcher/platform_dispatcher_test.dart index 85421d24f70ab..fba1d6cc51a8f 100644 --- a/lib/web_ui/test/canvaskit/platform_dispatcher_test.dart +++ b/lib/web_ui/test/engine/platform_dispatcher/platform_dispatcher_test.dart @@ -11,7 +11,7 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; -import 'common.dart'; +import '../../canvaskit/common.dart'; void main() { internalBootstrapBrowserTest(() => testMain); diff --git a/lib/web_ui/test/ui/system_ui_overlay_style_test.dart b/lib/web_ui/test/engine/platform_dispatcher/system_ui_overlay_style_test.dart similarity index 98% rename from lib/web_ui/test/ui/system_ui_overlay_style_test.dart rename to lib/web_ui/test/engine/platform_dispatcher/system_ui_overlay_style_test.dart index 36c5fab22c61d..867f73d61015b 100644 --- a/lib/web_ui/test/ui/system_ui_overlay_style_test.dart +++ b/lib/web_ui/test/engine/platform_dispatcher/system_ui_overlay_style_test.dart @@ -7,7 +7,7 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; -import 'utils.dart'; +import '../../ui/utils.dart'; void main() { internalBootstrapBrowserTest(() => testMain); From f29c65af0bb44f80545fd820c0567b9e459fdb6b Mon Sep 17 00:00:00 2001 From: maRci002 Date: Tue, 11 Apr 2023 16:21:37 +0200 Subject: [PATCH 4/7] fix typo --- lib/web_ui/lib/src/engine/platform_dispatcher.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index 04d128e282cd1..df246a8fcc0f2 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -524,7 +524,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { 'is deprecated on the web.\n' 'Use `SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(systemNavigationBarColor: ...)) ' 'instead.\n' - 'If you did not directly call the `SystemChrome.setSystemUIOverlayStyle` ' + 'If you did not directly call the `SystemChrome.setApplicationSwitcherDescription` ' 'method, you can ignore this message. It is most likely ' 'triggered by the `Title` widget, which is used by `MaterialApp` / `CupertinoApp` / `WidgetsApp`.\n' 'See: https://github.com/flutter/flutter/issues/123365'); From aab63010cc52dde0babf4ed5870573e40838e27c Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Tue, 11 Apr 2023 16:56:01 -0700 Subject: [PATCH 5/7] Place platform_dispatcher_test in their correct locations. --- .../canvaskit/platform_dispatcher_test.dart | 44 ++++ .../platform_dispatcher_test.dart | 189 ++++++++++++++- .../test/engine/platform_dispatcher_test.dart | 225 ------------------ 3 files changed, 229 insertions(+), 229 deletions(-) create mode 100644 lib/web_ui/test/canvaskit/platform_dispatcher_test.dart delete mode 100644 lib/web_ui/test/engine/platform_dispatcher_test.dart diff --git a/lib/web_ui/test/canvaskit/platform_dispatcher_test.dart b/lib/web_ui/test/canvaskit/platform_dispatcher_test.dart new file mode 100644 index 0000000000000..85421d24f70ab --- /dev/null +++ b/lib/web_ui/test/canvaskit/platform_dispatcher_test.dart @@ -0,0 +1,44 @@ +// Copyright 2013 The Flutter 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:async'; +import 'dart:typed_data'; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; + +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('PlatformDispatcher', () { + setUpCanvasKitTest(); + + test('responds to flutter/skia Skia.setResourceCacheMaxBytes', () async { + const MethodCodec codec = JSONMethodCodec(); + final Completer completer = Completer(); + ui.PlatformDispatcher.instance.sendPlatformMessage( + 'flutter/skia', + codec.encodeMethodCall(const MethodCall( + 'Skia.setResourceCacheMaxBytes', + 512 * 1000 * 1000, + )), + completer.complete, + ); + + final ByteData? response = await completer.future; + expect(response, isNotNull); + expect( + codec.decodeEnvelope(response!), + [true], + ); + }); + }); +} diff --git a/lib/web_ui/test/engine/platform_dispatcher/platform_dispatcher_test.dart b/lib/web_ui/test/engine/platform_dispatcher/platform_dispatcher_test.dart index fba1d6cc51a8f..7cecb112425c8 100644 --- a/lib/web_ui/test/engine/platform_dispatcher/platform_dispatcher_test.dart +++ b/lib/web_ui/test/engine/platform_dispatcher/platform_dispatcher_test.dart @@ -7,19 +7,31 @@ import 'dart:typed_data'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; - import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; -import '../../canvaskit/common.dart'; - void main() { internalBootstrapBrowserTest(() => testMain); } void testMain() { + ensureFlutterViewEmbedderInitialized(); + group('PlatformDispatcher', () { - setUpCanvasKitTest(); + test('high contrast in accessibilityFeatures has the correct value', () { + final MockHighContrastSupport mockHighContrast = + MockHighContrastSupport(); + HighContrastSupport.instance = mockHighContrast; + final EnginePlatformDispatcher engineDispatcher = + EnginePlatformDispatcher(); + + expect(engineDispatcher.accessibilityFeatures.highContrast, isTrue); + mockHighContrast.isEnabled = false; + mockHighContrast.invokeListeners(mockHighContrast.isEnabled); + expect(engineDispatcher.accessibilityFeatures.highContrast, isFalse); + + engineDispatcher.dispose(); + }); test('responds to flutter/skia Skia.setResourceCacheMaxBytes', () async { const MethodCodec codec = JSONMethodCodec(); @@ -40,5 +52,174 @@ void testMain() { [true], ); }); + + test('responds to flutter/platform HapticFeedback.vibrate', () async { + const MethodCodec codec = JSONMethodCodec(); + final Completer completer = Completer(); + ui.PlatformDispatcher.instance.sendPlatformMessage( + 'flutter/platform', + codec.encodeMethodCall(const MethodCall( + 'HapticFeedback.vibrate', + )), + completer.complete, + ); + + final ByteData? response = await completer.future; + expect(response, isNotNull); + expect( + codec.decodeEnvelope(response!), + true, + ); + }); + + test('responds to flutter/platform SystemChrome.setSystemUIOverlayStyle', + () async { + const MethodCodec codec = JSONMethodCodec(); + final Completer completer = Completer(); + ui.PlatformDispatcher.instance.sendPlatformMessage( + 'flutter/platform', + codec.encodeMethodCall(const MethodCall( + 'SystemChrome.setSystemUIOverlayStyle', + {}, + )), + completer.complete, + ); + + final ByteData? response = await completer.future; + expect(response, isNotNull); + expect( + codec.decodeEnvelope(response!), + true, + ); + }); + + test('responds to flutter/contextmenu enable', () async { + const MethodCodec codec = JSONMethodCodec(); + final Completer completer = Completer(); + ui.PlatformDispatcher.instance.sendPlatformMessage( + 'flutter/contextmenu', + codec.encodeMethodCall(const MethodCall( + 'enableContextMenu', + )), + completer.complete, + ); + + final ByteData? response = await completer.future; + expect(response, isNotNull); + expect( + codec.decodeEnvelope(response!), + true, + ); + }); + + test('responds to flutter/contextmenu disable', () async { + const MethodCodec codec = JSONMethodCodec(); + final Completer completer = Completer(); + ui.PlatformDispatcher.instance.sendPlatformMessage( + 'flutter/contextmenu', + codec.encodeMethodCall(const MethodCall( + 'disableContextMenu', + )), + completer.complete, + ); + + final ByteData? response = await completer.future; + expect(response, isNotNull); + expect( + codec.decodeEnvelope(response!), + true, + ); + }); + + test('can find text scale factor', () async { + const double deltaTolerance = 1e-5; + + final DomElement root = domDocument.documentElement!; + final String oldFontSize = root.style.fontSize; + + addTearDown(() { + root.style.fontSize = oldFontSize; + }); + + root.style.fontSize = '16px'; + expect(findBrowserTextScaleFactor(), 1.0); + + root.style.fontSize = '20px'; + expect(findBrowserTextScaleFactor(), 1.25); + + root.style.fontSize = '24px'; + expect(findBrowserTextScaleFactor(), 1.5); + + root.style.fontSize = '14.4px'; + expect(findBrowserTextScaleFactor(), closeTo(0.9, deltaTolerance)); + + root.style.fontSize = '12.8px'; + expect(findBrowserTextScaleFactor(), closeTo(0.8, deltaTolerance)); + + root.style.fontSize = ''; + expect(findBrowserTextScaleFactor(), 1.0); + }); + + test( + "calls onTextScaleFactorChanged when the element's font-size changes", + () async { + final DomElement root = domDocument.documentElement!; + final String oldFontSize = root.style.fontSize; + final ui.VoidCallback? oldCallback = + ui.PlatformDispatcher.instance.onTextScaleFactorChanged; + + addTearDown(() { + root.style.fontSize = oldFontSize; + ui.PlatformDispatcher.instance.onTextScaleFactorChanged = oldCallback; + }); + + root.style.fontSize = '16px'; + + bool isCalled = false; + ui.PlatformDispatcher.instance.onTextScaleFactorChanged = () { + isCalled = true; + }; + + root.style.fontSize = '20px'; + await Future.delayed(Duration.zero); + expect(root.style.fontSize, '20px'); + expect(isCalled, isTrue); + expect(ui.PlatformDispatcher.instance.textScaleFactor, + findBrowserTextScaleFactor()); + + isCalled = false; + + root.style.fontSize = '16px'; + await Future.delayed(Duration.zero); + expect(root.style.fontSize, '16px'); + expect(isCalled, isTrue); + expect(ui.PlatformDispatcher.instance.textScaleFactor, + findBrowserTextScaleFactor()); + }); }); } + +class MockHighContrastSupport implements HighContrastSupport { + bool isEnabled = true; + + final List _listeners = []; + + @override + bool get isHighContrastEnabled => isEnabled; + + void invokeListeners(bool val) { + for (final HighContrastListener listener in _listeners) { + listener(val); + } + } + + @override + void addListener(HighContrastListener listener) { + _listeners.add(listener); + } + + @override + void removeListener(HighContrastListener listener) { + _listeners.remove(listener); + } +} diff --git a/lib/web_ui/test/engine/platform_dispatcher_test.dart b/lib/web_ui/test/engine/platform_dispatcher_test.dart deleted file mode 100644 index 7cecb112425c8..0000000000000 --- a/lib/web_ui/test/engine/platform_dispatcher_test.dart +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright 2013 The Flutter 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:async'; -import 'dart:typed_data'; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' as ui; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() { - ensureFlutterViewEmbedderInitialized(); - - group('PlatformDispatcher', () { - test('high contrast in accessibilityFeatures has the correct value', () { - final MockHighContrastSupport mockHighContrast = - MockHighContrastSupport(); - HighContrastSupport.instance = mockHighContrast; - final EnginePlatformDispatcher engineDispatcher = - EnginePlatformDispatcher(); - - expect(engineDispatcher.accessibilityFeatures.highContrast, isTrue); - mockHighContrast.isEnabled = false; - mockHighContrast.invokeListeners(mockHighContrast.isEnabled); - expect(engineDispatcher.accessibilityFeatures.highContrast, isFalse); - - engineDispatcher.dispose(); - }); - - test('responds to flutter/skia Skia.setResourceCacheMaxBytes', () async { - const MethodCodec codec = JSONMethodCodec(); - final Completer completer = Completer(); - ui.PlatformDispatcher.instance.sendPlatformMessage( - 'flutter/skia', - codec.encodeMethodCall(const MethodCall( - 'Skia.setResourceCacheMaxBytes', - 512 * 1000 * 1000, - )), - completer.complete, - ); - - final ByteData? response = await completer.future; - expect(response, isNotNull); - expect( - codec.decodeEnvelope(response!), - [true], - ); - }); - - test('responds to flutter/platform HapticFeedback.vibrate', () async { - const MethodCodec codec = JSONMethodCodec(); - final Completer completer = Completer(); - ui.PlatformDispatcher.instance.sendPlatformMessage( - 'flutter/platform', - codec.encodeMethodCall(const MethodCall( - 'HapticFeedback.vibrate', - )), - completer.complete, - ); - - final ByteData? response = await completer.future; - expect(response, isNotNull); - expect( - codec.decodeEnvelope(response!), - true, - ); - }); - - test('responds to flutter/platform SystemChrome.setSystemUIOverlayStyle', - () async { - const MethodCodec codec = JSONMethodCodec(); - final Completer completer = Completer(); - ui.PlatformDispatcher.instance.sendPlatformMessage( - 'flutter/platform', - codec.encodeMethodCall(const MethodCall( - 'SystemChrome.setSystemUIOverlayStyle', - {}, - )), - completer.complete, - ); - - final ByteData? response = await completer.future; - expect(response, isNotNull); - expect( - codec.decodeEnvelope(response!), - true, - ); - }); - - test('responds to flutter/contextmenu enable', () async { - const MethodCodec codec = JSONMethodCodec(); - final Completer completer = Completer(); - ui.PlatformDispatcher.instance.sendPlatformMessage( - 'flutter/contextmenu', - codec.encodeMethodCall(const MethodCall( - 'enableContextMenu', - )), - completer.complete, - ); - - final ByteData? response = await completer.future; - expect(response, isNotNull); - expect( - codec.decodeEnvelope(response!), - true, - ); - }); - - test('responds to flutter/contextmenu disable', () async { - const MethodCodec codec = JSONMethodCodec(); - final Completer completer = Completer(); - ui.PlatformDispatcher.instance.sendPlatformMessage( - 'flutter/contextmenu', - codec.encodeMethodCall(const MethodCall( - 'disableContextMenu', - )), - completer.complete, - ); - - final ByteData? response = await completer.future; - expect(response, isNotNull); - expect( - codec.decodeEnvelope(response!), - true, - ); - }); - - test('can find text scale factor', () async { - const double deltaTolerance = 1e-5; - - final DomElement root = domDocument.documentElement!; - final String oldFontSize = root.style.fontSize; - - addTearDown(() { - root.style.fontSize = oldFontSize; - }); - - root.style.fontSize = '16px'; - expect(findBrowserTextScaleFactor(), 1.0); - - root.style.fontSize = '20px'; - expect(findBrowserTextScaleFactor(), 1.25); - - root.style.fontSize = '24px'; - expect(findBrowserTextScaleFactor(), 1.5); - - root.style.fontSize = '14.4px'; - expect(findBrowserTextScaleFactor(), closeTo(0.9, deltaTolerance)); - - root.style.fontSize = '12.8px'; - expect(findBrowserTextScaleFactor(), closeTo(0.8, deltaTolerance)); - - root.style.fontSize = ''; - expect(findBrowserTextScaleFactor(), 1.0); - }); - - test( - "calls onTextScaleFactorChanged when the element's font-size changes", - () async { - final DomElement root = domDocument.documentElement!; - final String oldFontSize = root.style.fontSize; - final ui.VoidCallback? oldCallback = - ui.PlatformDispatcher.instance.onTextScaleFactorChanged; - - addTearDown(() { - root.style.fontSize = oldFontSize; - ui.PlatformDispatcher.instance.onTextScaleFactorChanged = oldCallback; - }); - - root.style.fontSize = '16px'; - - bool isCalled = false; - ui.PlatformDispatcher.instance.onTextScaleFactorChanged = () { - isCalled = true; - }; - - root.style.fontSize = '20px'; - await Future.delayed(Duration.zero); - expect(root.style.fontSize, '20px'); - expect(isCalled, isTrue); - expect(ui.PlatformDispatcher.instance.textScaleFactor, - findBrowserTextScaleFactor()); - - isCalled = false; - - root.style.fontSize = '16px'; - await Future.delayed(Duration.zero); - expect(root.style.fontSize, '16px'); - expect(isCalled, isTrue); - expect(ui.PlatformDispatcher.instance.textScaleFactor, - findBrowserTextScaleFactor()); - }); - }); -} - -class MockHighContrastSupport implements HighContrastSupport { - bool isEnabled = true; - - final List _listeners = []; - - @override - bool get isHighContrastEnabled => isEnabled; - - void invokeListeners(bool val) { - for (final HighContrastListener listener in _listeners) { - listener(val); - } - } - - @override - void addListener(HighContrastListener listener) { - _listeners.add(listener); - } - - @override - void removeListener(HighContrastListener listener) { - _listeners.remove(listener); - } -} From 14cca60b06acaba787dafafa50fec0f5a96b173e Mon Sep 17 00:00:00 2001 From: maRci002 Date: Thu, 13 Apr 2023 11:59:46 +0200 Subject: [PATCH 6/7] Show warning message in debug mode --- .../lib/src/engine/platform_dispatcher.dart | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index df246a8fcc0f2..eedb6782521bb 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -517,18 +517,19 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { final Map arguments = decoded.arguments as Map; // TODO(ferhat): Find more appropriate defaults? Or noop when values are null? final String label = arguments['label'] as String? ?? ''; - final int? primaryColor = arguments['primaryColor'] as int?; - if (primaryColor != null) { - domWindow.console.warn( - 'The `primaryColor` in `SystemChrome.setApplicationSwitcherDescription` ' - 'is deprecated on the web.\n' - 'Use `SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(systemNavigationBarColor: ...)) ' - 'instead.\n' - 'If you did not directly call the `SystemChrome.setApplicationSwitcherDescription` ' - 'method, you can ignore this message. It is most likely ' - 'triggered by the `Title` widget, which is used by `MaterialApp` / `CupertinoApp` / `WidgetsApp`.\n' - 'See: https://github.com/flutter/flutter/issues/123365'); - } + assert(() { + final int? primaryColor = arguments['primaryColor'] as int?; + if (primaryColor != null) { + domWindow.console.warn( + 'The `primaryColor` in `SystemChrome.setApplicationSwitcherDescription is deprecated on the web.\n' + 'Use `SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(systemNavigationBarColor: ...)) instead.\n' + 'If you did not directly call the `SystemChrome.setApplicationSwitcherDescription` method, you can ignore this message. ' + 'It is most likely triggered by the `Title` widget, which is used by `MaterialApp` / `CupertinoApp` / `WidgetsApp`.\n' + 'See: https://github.com/flutter/flutter/issues/123365', + ); + } + return true; + }()); domDocument.title = label; replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; From 4ddbb84ea37a4fc34b6bcbbd4cb3dbf4030513a2 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Thu, 13 Apr 2023 18:20:42 -0700 Subject: [PATCH 7/7] Restore old functionality so this is backwards compatible. --- .../lib/src/engine/platform_dispatcher.dart | 17 +-- ...application_switcher_description_test.dart | 110 ++++++++++++++++++ .../system_ui_overlay_style_test.dart | 14 +-- lib/web_ui/test/ui/title_test.dart | 89 -------------- 4 files changed, 120 insertions(+), 110 deletions(-) create mode 100644 lib/web_ui/test/engine/platform_dispatcher/application_switcher_description_test.dart delete mode 100644 lib/web_ui/test/ui/title_test.dart diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index eedb6782521bb..ad127afb6c184 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -515,22 +515,11 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { return; case 'SystemChrome.setApplicationSwitcherDescription': final Map arguments = decoded.arguments as Map; - // TODO(ferhat): Find more appropriate defaults? Or noop when values are null? final String label = arguments['label'] as String? ?? ''; - assert(() { - final int? primaryColor = arguments['primaryColor'] as int?; - if (primaryColor != null) { - domWindow.console.warn( - 'The `primaryColor` in `SystemChrome.setApplicationSwitcherDescription is deprecated on the web.\n' - 'Use `SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(systemNavigationBarColor: ...)) instead.\n' - 'If you did not directly call the `SystemChrome.setApplicationSwitcherDescription` method, you can ignore this message. ' - 'It is most likely triggered by the `Title` widget, which is used by `MaterialApp` / `CupertinoApp` / `WidgetsApp`.\n' - 'See: https://github.com/flutter/flutter/issues/123365', - ); - } - return true; - }()); + // TODO(web): Stop setting the color from here, https://github.com/flutter/flutter/issues/123365 + final int primaryColor = arguments['primaryColor'] as int? ?? 0xFF000000; domDocument.title = label; + setThemeColor(ui.Color(primaryColor)); replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; case 'SystemChrome.setSystemUIOverlayStyle': diff --git a/lib/web_ui/test/engine/platform_dispatcher/application_switcher_description_test.dart b/lib/web_ui/test/engine/platform_dispatcher/application_switcher_description_test.dart new file mode 100644 index 0000000000000..5d3238402abc1 --- /dev/null +++ b/lib/web_ui/test/engine/platform_dispatcher/application_switcher_description_test.dart @@ -0,0 +1,110 @@ +// Copyright 2013 The Flutter 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 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + ensureFlutterViewEmbedderInitialized(); + + String? getCssThemeColor() { + final DomHTMLMetaElement? theme = + domDocument.querySelector('#flutterweb-theme') as DomHTMLMetaElement?; + return theme?.content; + } + + const MethodCodec codec = JSONMethodCodec(); + + group('Title and Primary Color/Theme meta', () { + test('is set on the document by platform message', () { + // Run the unit test without emulating Flutter tester environment. + ui.debugEmulateFlutterTesterEnvironment = false; + + // TODO(yjbanov): https://github.com/flutter/flutter/issues/39159 + domDocument.title = ''; + expect(domDocument.title, ''); + expect(getCssThemeColor(), isNull); + + ui.window.sendPlatformMessage( + 'flutter/platform', + codec.encodeMethodCall(const MethodCall( + 'SystemChrome.setApplicationSwitcherDescription', + { + 'label': 'Title Test', + 'primaryColor': 0xFF00FF00, + }, + )), + null, + ); + + const ui.Color expectedPrimaryColor = ui.Color(0xFF00FF00); + + expect(domDocument.title, 'Title Test'); + expect(getCssThemeColor(), colorToCssString(expectedPrimaryColor)); + + ui.window.sendPlatformMessage( + 'flutter/platform', + codec.encodeMethodCall(const MethodCall( + 'SystemChrome.setApplicationSwitcherDescription', + { + 'label': 'Different title', + 'primaryColor': 0xFFFABADA, + }, + )), + null, + ); + + const ui.Color expectedNewPrimaryColor = ui.Color(0xFFFABADA); + + expect(domDocument.title, 'Different title'); + expect(getCssThemeColor(), colorToCssString(expectedNewPrimaryColor)); + }); + + test('supports null title and primaryColor', () { + // Run the unit test without emulating Flutter tester environment. + ui.debugEmulateFlutterTesterEnvironment = false; + + const ui.Color expectedNullColor = ui.Color(0xFF000000); + // TODO(yjbanov): https://github.com/flutter/flutter/issues/39159 + domDocument.title = 'Something Else'; + expect(domDocument.title, 'Something Else'); + + ui.window.sendPlatformMessage( + 'flutter/platform', + codec.encodeMethodCall(const MethodCall( + 'SystemChrome.setApplicationSwitcherDescription', + { + 'label': null, + 'primaryColor': null, + }, + )), + null, + ); + + expect(domDocument.title, ''); + expect(getCssThemeColor(), colorToCssString(expectedNullColor)); + + domDocument.title = 'Something Else'; + expect(domDocument.title, 'Something Else'); + + ui.window.sendPlatformMessage( + 'flutter/platform', + codec.encodeMethodCall(const MethodCall( + 'SystemChrome.setApplicationSwitcherDescription', + {}, + )), + null, + ); + + expect(domDocument.title, ''); + expect(getCssThemeColor(), colorToCssString(expectedNullColor)); + }); + }); +} diff --git a/lib/web_ui/test/engine/platform_dispatcher/system_ui_overlay_style_test.dart b/lib/web_ui/test/engine/platform_dispatcher/system_ui_overlay_style_test.dart index 867f73d61015b..b79a7b429dbe4 100644 --- a/lib/web_ui/test/engine/platform_dispatcher/system_ui_overlay_style_test.dart +++ b/lib/web_ui/test/engine/platform_dispatcher/system_ui_overlay_style_test.dart @@ -7,24 +7,24 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; -import '../../ui/utils.dart'; - void main() { internalBootstrapBrowserTest(() => testMain); } void testMain() { - setUpUiTest(); + ensureFlutterViewEmbedderInitialized(); const MethodCodec codec = JSONMethodCodec(); void sendSetSystemUIOverlayStyle({ui.Color? statusBarColor}) { ui.window.sendPlatformMessage( 'flutter/platform', - codec.encodeMethodCall( - MethodCall('SystemChrome.setSystemUIOverlayStyle', { - 'statusBarColor': statusBarColor?.value, - })), + codec.encodeMethodCall(MethodCall( + 'SystemChrome.setSystemUIOverlayStyle', + { + 'statusBarColor': statusBarColor?.value, + }, + )), null, ); } diff --git a/lib/web_ui/test/ui/title_test.dart b/lib/web_ui/test/ui/title_test.dart deleted file mode 100644 index 13ac803f7e14e..0000000000000 --- a/lib/web_ui/test/ui/title_test.dart +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2013 The Flutter 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 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' as ui; - -import 'utils.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - setUpUiTest(); - - const MethodCodec codec = JSONMethodCodec(); - - group('Title', () { - test('is set on the document by platform message', () { - // Run the unit test without emulating Flutter tester environment. - ui.debugEmulateFlutterTesterEnvironment = false; - - // TODO(yjbanov): https://github.com/flutter/flutter/issues/39159 - domDocument.title = ''; - expect(domDocument.title, ''); - - ui.window.sendPlatformMessage( - 'flutter/platform', - codec.encodeMethodCall(const MethodCall( - 'SystemChrome.setApplicationSwitcherDescription', - { - 'label': 'Title Test', - 'primaryColor': 0xFF00FF00, - })), - null); - - expect(domDocument.title, 'Title Test'); - - ui.window.sendPlatformMessage( - 'flutter/platform', - codec.encodeMethodCall(const MethodCall( - 'SystemChrome.setApplicationSwitcherDescription', - { - 'label': 'Different title', - 'primaryColor': 0xFF00FF00, - })), - null); - - expect(domDocument.title, 'Different title'); - }); - - test('supports null title and primaryColor', () { - // Run the unit test without emulating Flutter tester environment. - ui.debugEmulateFlutterTesterEnvironment = false; - - // TODO(yjbanov): https://github.com/flutter/flutter/issues/39159 - domDocument.title = 'Something Else'; - expect(domDocument.title, 'Something Else'); - - ui.window.sendPlatformMessage( - 'flutter/platform', - codec.encodeMethodCall(const MethodCall( - 'SystemChrome.setApplicationSwitcherDescription', - { - 'label': null, - 'primaryColor': null, - })), - null); - - expect(domDocument.title, ''); - - domDocument.title = 'Something Else'; - expect(domDocument.title, 'Something Else'); - - ui.window.sendPlatformMessage( - 'flutter/platform', - codec.encodeMethodCall(const MethodCall( - 'SystemChrome.setApplicationSwitcherDescription', - { - })), - null); - - expect(domDocument.title, ''); - }); - }); -}