diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index efe4496e8bd06..ad127afb6c184 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -514,14 +514,20 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; case 'SystemChrome.setApplicationSwitcherDescription': - final Map arguments = decoded.arguments as Map; - // TODO(ferhat): Find more appropriate defaults? Or noop when values are null? + final Map arguments = decoded.arguments as Map; final String label = arguments['label'] as String? ?? ''; + // 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': + 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': final List arguments = decoded.arguments as List; flutterViewEmbedder.setPreferredOrientation(arguments).then((bool success) { diff --git a/lib/web_ui/lib/src/engine/util.dart b/lib/web_ui/lib/src/engine/util.dart index a10f5f4674cd1..0f81e9dcaea79 100644 --- a/lib/web_ui/lib/src/engine/util.dart +++ b/lib/web_ui/lib/src/engine/util.dart @@ -679,16 +679,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 { + theme?.remove(); } - theme.content = colorToCssString(color)!; } bool? _ellipseFeatureDetected; 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_test.dart b/lib/web_ui/test/engine/platform_dispatcher/platform_dispatcher_test.dart similarity index 85% rename from lib/web_ui/test/engine/platform_dispatcher_test.dart rename to lib/web_ui/test/engine/platform_dispatcher/platform_dispatcher_test.dart index 1b19330b3870d..7cecb112425c8 100644 --- a/lib/web_ui/test/engine/platform_dispatcher_test.dart +++ b/lib/web_ui/test/engine/platform_dispatcher/platform_dispatcher_test.dart @@ -72,6 +72,27 @@ 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(); @@ -144,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; @@ -162,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; @@ -170,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()); }); }); } @@ -183,7 +207,6 @@ class MockHighContrastSupport implements HighContrastSupport { @override bool get isHighContrastEnabled => isEnabled; - void invokeListeners(bool val) { for (final HighContrastListener listener in _listeners) { listener(val); 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 new file mode 100644 index 0000000000000..b79a7b429dbe4 --- /dev/null +++ b/lib/web_ui/test/engine/platform_dispatcher/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; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + ensureFlutterViewEmbedderInitialized(); + + 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); + }); + }); +} 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, ''); - }); - }); -}