From b9da2bd0b3c340ab1c5a7fb82ae3fc4860e39a6a Mon Sep 17 00:00:00 2001 From: Adrien ERAUD Date: Thu, 2 Dec 2021 14:35:53 +0000 Subject: [PATCH 1/5] Add a backgroundColor option to the iOS webview --- .../webview_flutter_wkwebview/CHANGELOG.md | 5 +- .../ios/RunnerUITests/FLTWebViewUITests.m | 67 +++++++++++++++++++ .../example/lib/main.dart | 44 +++++++++++- .../example/lib/web_view.dart | 9 +++ .../example/pubspec.yaml | 3 +- .../ios/Classes/FlutterWebView.m | 14 ++++ .../webview_flutter_wkwebview/pubspec.yaml | 6 +- 7 files changed, 139 insertions(+), 9 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index e9c59c26c3ce..22f45f27c4d7 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.5.0 +* Adds an option to set the background color of the webview. * Migrates from `analysis_options_legacy.yaml` to `analysis_options.yaml`. * Integration test fixes. * Updates to webview_flutter_platform_interface version 1.5.2. @@ -22,7 +23,7 @@ ## 2.0.14 -* Update example App so navigation menu loads immediatly but only becomes available when `WebViewController` is available (same behavior as example App in webview_flutter package). +* Update example App so navigation menu loads immediatly but only becomes available when `WebViewController` is available (same behavior as example App in webview_flutter package). ## 2.0.13 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerUITests/FLTWebViewUITests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerUITests/FLTWebViewUITests.m index d193be745972..e43fb97d41ef 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerUITests/FLTWebViewUITests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerUITests/FLTWebViewUITests.m @@ -5,6 +5,25 @@ @import XCTest; @import os.log; +static UIColor* getPixelColorInImage(CGImageRef image, size_t x, size_t y) { + CFDataRef pixelData = CGDataProviderCopyData(CGImageGetDataProvider(image)); + const UInt8* data = CFDataGetBytePtr(pixelData); + + size_t bytesPerRow = CGImageGetBytesPerRow(image); + size_t pixelInfo = (bytesPerRow * y) + (x * 4); // 4 bytes per pixel + + UInt8 red = data[pixelInfo + 0]; + UInt8 green = data[pixelInfo + 1]; + UInt8 blue = data[pixelInfo + 2]; + UInt8 alpha = data[pixelInfo + 3]; + CFRelease(pixelData); + + return [UIColor colorWithRed:red / 255.0f + green:green / 255.0f + blue:blue / 255.0f + alpha:alpha / 255.0f]; +} + @interface FLTWebViewUITests : XCTestCase @property(nonatomic, strong) XCUIApplication* app; @end @@ -18,6 +37,54 @@ - (void)setUp { [self.app launch]; } +- (void)testTransparentBackground { + XCUIApplication* app = self.app; + XCUIElement* menu = app.buttons[@"Show menu"]; + if (![menu waitForExistenceWithTimeout:30.0]) { + os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); + XCTFail(@"Failed due to not able to find menu"); + } + [menu tap]; + + XCUIElement* transparentBackground = app.buttons[@"Transparent background example"]; + if (![transparentBackground waitForExistenceWithTimeout:30.0]) { + os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); + XCTFail(@"Failed due to not able to find Transparent background example"); + } + [transparentBackground tap]; + + XCUIElement* transparentBackgroundLoaded = + app.webViews.staticTexts[@"Transparent background test"]; + if (![transparentBackgroundLoaded waitForExistenceWithTimeout:30.0]) { + os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); + XCTFail(@"Failed due to not able to find Transparent background test"); + } + + XCUIScreenshot* screenshot = [[XCUIScreen mainScreen] screenshot]; + + UIImage* screenshotImage = screenshot.image; + CGImageRef screenshotCGImage = screenshotImage.CGImage; + UIColor* centerLeftColor = + getPixelColorInImage(screenshotCGImage, 0, CGImageGetHeight(screenshotCGImage) / 2); + UIColor* centerColor = + getPixelColorInImage(screenshotCGImage, CGImageGetWidth(screenshotCGImage) / 2, + CGImageGetHeight(screenshotCGImage) / 2); + + CGColorSpaceRef centerLeftColorSpace = CGColorGetColorSpace(centerLeftColor.CGColor); + // Flutter Colors.green color : 0xFF4CAF50 -> rgba(76, 175, 80, 1) + // https://github.com/flutter/flutter/blob/f4abaa0735eba4dfd8f33f73363911d63931fe03/packages/flutter/lib/src/material/colors.dart#L1208 + // The background color of the webview is : rgba(0, 0, 0, 0.5) + // The expected color is : rgba(38, 87, 40, 1) + CGFloat flutterGreenColorComponents[] = {38.0f / 255.0f, 87.0f / 255.0f, 40.0f / 255.0f, 1.0f}; + CGColorRef flutterGreenColor = CGColorCreate(centerLeftColorSpace, flutterGreenColorComponents); + CGFloat redColorComponents[] = {1.0f, 0.0f, 0.0f, 1.0f}; + CGColorRef redColor = CGColorCreate(centerLeftColorSpace, redColorComponents); + CGColorSpaceRelease(centerLeftColorSpace); + + XCTAssertTrue(CGColorEqualToColor(flutterGreenColor, centerLeftColor.CGColor)); + XCTAssertTrue(CGColorEqualToColor(redColor, centerColor.CGColor)); +} + - (void)testUserAgent { XCUIApplication* app = self.app; XCUIElement* menu = app.buttons[@"Show menu"]; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart index df85d42bea8f..988945615201 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -37,6 +37,27 @@ The navigation delegate is set to block navigation to the youtube website. '''; +const String kTransparentBackgroundPage = ''' + + + + Transparent background test + + + +
+

Transparent background test

+
+
+ + +'''; + const String kLocalFileExamplePage = ''' @@ -47,8 +68,8 @@ const String kLocalFileExamplePage = '''

Local demo page

- This is an example page used to demonstrate how to load a local file or HTML - string using the Flutter + This is an example page used to demonstrate how to load a local file or HTML + string using the Flutter webview plugin.

@@ -70,6 +91,7 @@ class _WebViewExampleState extends State<_WebViewExample> { @override Widget build(BuildContext context) { return Scaffold( + backgroundColor: Colors.green, appBar: AppBar( title: const Text('Flutter WebView example'), // This drop down menu demonstrates that Flutter widgets can be shown over the web view. @@ -96,6 +118,7 @@ class _WebViewExampleState extends State<_WebViewExample> { print('allowing navigation to $request'); return NavigationDecision.navigate; }, + backgroundColor: const Color(0x80000000), ); }), floatingActionButton: favoriteButton(), @@ -146,6 +169,7 @@ enum _MenuOptions { loadLocalFile, loadHtmlString, doPostRequest, + transparentBackground, } class _SampleMenu extends StatelessWidget { @@ -160,6 +184,7 @@ class _SampleMenu extends StatelessWidget { builder: (BuildContext context, AsyncSnapshot controller) { return PopupMenuButton<_MenuOptions>( + key: const ValueKey('ShowPopupMenu'), onSelected: (_MenuOptions value) { switch (value) { case _MenuOptions.showUserAgent: @@ -192,6 +217,9 @@ class _SampleMenu extends StatelessWidget { case _MenuOptions.doPostRequest: _onDoPostRequest(controller.data!, context); break; + case _MenuOptions.transparentBackground: + _onTransparentBackground(controller.data!, context); + break; } }, itemBuilder: (BuildContext context) => >[ @@ -236,6 +264,11 @@ class _SampleMenu extends StatelessWidget { value: _MenuOptions.doPostRequest, child: Text('Post Request'), ), + const PopupMenuItem<_MenuOptions>( + key: ValueKey('ShowTransparentBackgroundExample'), + value: _MenuOptions.transparentBackground, + child: Text('Transparent background example'), + ), ], ); }, @@ -346,6 +379,13 @@ class _SampleMenu extends StatelessWidget { ); } + Future _onTransparentBackground( + WebViewController controller, BuildContext context) async { + final String contentBase64 = + base64Encode(const Utf8Encoder().convert(kTransparentBackgroundPage)); + await controller.loadUrl('data:text/html;base64,$contentBase64'); + } + static Future _prepareLocalFile() async { final String tmpDir = (await getTemporaryDirectory()).path; final File indexFile = File('$tmpDir/www/index.html'); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart index ae540ae23c2d..41e726d5d794 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart @@ -69,6 +69,7 @@ class WebView extends StatefulWidget { this.initialMediaPlaybackPolicy = AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, this.allowsInlineMediaPlayback = false, + this.backgroundColor, }) : assert(javascriptMode != null), assert(initialMediaPlaybackPolicy != null), assert(allowsInlineMediaPlayback != null), @@ -227,6 +228,13 @@ class WebView extends StatefulWidget { /// The default policy is [AutoMediaPlaybackPolicy.require_user_action_for_all_media_types]. final AutoMediaPlaybackPolicy initialMediaPlaybackPolicy; + /// The background color of the webview. + /// + /// When null the platform's webview default background color is used. + /// + /// By default `backgroundColor` is null. + final Color? backgroundColor; + @override _WebViewState createState() => _WebViewState(); } @@ -278,6 +286,7 @@ class _WebViewState extends State { _javascriptChannelRegistry.channels.keys.toSet(), autoMediaPlaybackPolicy: widget.initialMediaPlaybackPolicy, userAgent: widget.userAgent, + backgroundColor: widget.backgroundColor, ), javascriptChannelRegistry: _javascriptChannelRegistry, ); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml index 2888cddac552..2070b1161ba0 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml @@ -10,7 +10,7 @@ dependencies: sdk: flutter path_provider: ^2.0.6 - + webview_flutter_wkwebview: # When depending on this package from a real application you should use: # webview_flutter: ^x.y.z @@ -34,4 +34,3 @@ flutter: assets: - assets/sample_audio.ogg - assets/sample_video.mp4 - \ No newline at end of file diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m index 351d1ae58760..eff7c68b409f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m @@ -96,6 +96,20 @@ - (instancetype)initWithFrame:(CGRect)frame inConfiguration:configuration]; _webView = [[FLTWKWebView alloc] initWithFrame:frame configuration:configuration]; + + // Background color + NSNumber* backgroundColorNSNumber = args[@"backgroundColor"]; + if ([backgroundColorNSNumber isKindOfClass:[NSNumber class]]) { + int backgroundColorInt = [backgroundColorNSNumber intValue]; + UIColor* backgroundColor = [UIColor colorWithRed:(backgroundColorInt >> 16 & 0xff) / 255.f + green:(backgroundColorInt >> 8 & 0xff) / 255.f + blue:(backgroundColorInt & 0xff) / 255.f + alpha:(backgroundColorInt >> 24 & 0xff) / 255.f]; + _webView.opaque = NO; + _webView.backgroundColor = UIColor.clearColor; + _webView.scrollView.backgroundColor = backgroundColor; + } + _navigationDelegate = [[FLTWKNavigationDelegate alloc] initWithChannel:_channel]; _webView.UIDelegate = self; _webView.navigationDelegate = _navigationDelegate; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index a152e2ba2882..702473382793 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_wkwebview description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control. repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_wkwebview issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.4.0 +version: 2.5.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -18,11 +18,11 @@ flutter: dependencies: flutter: sdk: flutter - webview_flutter_platform_interface: ^1.5.2 + webview_flutter_platform_interface: ^1.7.0 dev_dependencies: flutter_driver: sdk: flutter flutter_test: sdk: flutter - pedantic: ^1.10.0 \ No newline at end of file + pedantic: ^1.10.0 From 9a0bb3a7b3803180146758df6f1604232b5a95f8 Mon Sep 17 00:00:00 2001 From: Adrien ERAUD Date: Sat, 4 Dec 2021 10:14:01 +0000 Subject: [PATCH 2/5] Hard code Colors.green to prevent test breaking if the color changes in the framework --- .../webview_flutter_wkwebview/example/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart index 988945615201..4b137966af62 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -91,7 +91,7 @@ class _WebViewExampleState extends State<_WebViewExample> { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Colors.green, + backgroundColor: const Color(0xFF4CAF50), appBar: AppBar( title: const Text('Flutter WebView example'), // This drop down menu demonstrates that Flutter widgets can be shown over the web view. From 4aa1dc22bc48659ec9042c70b7985e99aac51c6a Mon Sep 17 00:00:00 2001 From: Adrien ERAUD Date: Sat, 4 Dec 2021 10:16:28 +0000 Subject: [PATCH 3/5] Replace loadUrl by loadHtmlString in the example --- .../webview_flutter_wkwebview/example/lib/main.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart index 4b137966af62..41297bfd94fe 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -381,9 +381,7 @@ class _SampleMenu extends StatelessWidget { Future _onTransparentBackground( WebViewController controller, BuildContext context) async { - final String contentBase64 = - base64Encode(const Utf8Encoder().convert(kTransparentBackgroundPage)); - await controller.loadUrl('data:text/html;base64,$contentBase64'); + await controller.loadHtmlString(kTransparentBackgroundPage); } static Future _prepareLocalFile() async { From 7879884605d443014362d09a2bed429e3fd94f58 Mon Sep 17 00:00:00 2001 From: Adrien ERAUD Date: Sat, 4 Dec 2021 10:22:08 +0000 Subject: [PATCH 4/5] Nits --- .../webview_flutter_wkwebview/example/lib/web_view.dart | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart index 41e726d5d794..69fa2bdb79e3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart @@ -228,11 +228,10 @@ class WebView extends StatefulWidget { /// The default policy is [AutoMediaPlaybackPolicy.require_user_action_for_all_media_types]. final AutoMediaPlaybackPolicy initialMediaPlaybackPolicy; - /// The background color of the webview. + /// The background color of the [WebView]. /// - /// When null the platform's webview default background color is used. - /// - /// By default `backgroundColor` is null. + /// When `null` the platform's webview default background color is used. By + /// default [backgroundColor] is `null`. final Color? backgroundColor; @override From 27bb49c9466e0eebfa3f6f717c14db6fa708f0df Mon Sep 17 00:00:00 2001 From: Adrien ERAUD Date: Sat, 4 Dec 2021 10:42:00 +0000 Subject: [PATCH 5/5] Nit --- .../ios/Classes/FlutterWebView.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m index eff7c68b409f..25a93a67addc 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m @@ -101,10 +101,10 @@ - (instancetype)initWithFrame:(CGRect)frame NSNumber* backgroundColorNSNumber = args[@"backgroundColor"]; if ([backgroundColorNSNumber isKindOfClass:[NSNumber class]]) { int backgroundColorInt = [backgroundColorNSNumber intValue]; - UIColor* backgroundColor = [UIColor colorWithRed:(backgroundColorInt >> 16 & 0xff) / 255.f - green:(backgroundColorInt >> 8 & 0xff) / 255.f - blue:(backgroundColorInt & 0xff) / 255.f - alpha:(backgroundColorInt >> 24 & 0xff) / 255.f]; + UIColor* backgroundColor = [UIColor colorWithRed:(backgroundColorInt >> 16 & 0xff) / 255.0 + green:(backgroundColorInt >> 8 & 0xff) / 255.0 + blue:(backgroundColorInt & 0xff) / 255.0 + alpha:(backgroundColorInt >> 24 & 0xff) / 255.0]; _webView.opaque = NO; _webView.backgroundColor = UIColor.clearColor; _webView.scrollView.backgroundColor = backgroundColor;