Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[webview_flutter] Add a backgroundColor option to the iOS webview #4570

Merged
merged 5 commits into from
Dec 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,27 @@ The navigation delegate is set to block navigation to the youtube website.
</html>
''';

const String kTransparentBackgroundPage = '''
<!DOCTYPE html>
<html>
<head>
<title>Transparent background test</title>
</head>
<style type="text/css">
body { background: transparent; margin: 0; padding: 0; }
#container { position: relative; margin: 0; padding: 0; width: 100vw; height: 100vh; }
#shape { background: #FF0000; width: 200px; height: 100%; margin: 0; padding: 0; position: absolute; top: 0; bottom: 0; left: calc(50% - 100px); }
p { text-align: center; }
</style>
<body>
<div id="container">
<p>Transparent background test</p>
<div id="shape"></div>
</div>
</body>
</html>
''';

const String kLocalFileExamplePage = '''
<!DOCTYPE html>
<html lang="en">
Expand All @@ -47,8 +68,8 @@ const String kLocalFileExamplePage = '''

<h1>Local demo page</h1>
<p>
This is an example page used to demonstrate how to load a local file or HTML
string using the <a href="https://pub.dev/packages/webview_flutter">Flutter
This is an example page used to demonstrate how to load a local file or HTML
string using the <a href="https://pub.dev/packages/webview_flutter">Flutter
webview</a> plugin.
</p>

Expand All @@ -70,6 +91,7 @@ class _WebViewExampleState extends State<_WebViewExample> {
@override
Widget build(BuildContext context) {
return Scaffold(
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.
Expand All @@ -96,6 +118,7 @@ class _WebViewExampleState extends State<_WebViewExample> {
print('allowing navigation to $request');
return NavigationDecision.navigate;
},
backgroundColor: const Color(0x80000000),
);
}),
floatingActionButton: favoriteButton(),
Expand Down Expand Up @@ -146,6 +169,7 @@ enum _MenuOptions {
loadLocalFile,
loadHtmlString,
doPostRequest,
transparentBackground,
}

class _SampleMenu extends StatelessWidget {
Expand All @@ -160,6 +184,7 @@ class _SampleMenu extends StatelessWidget {
builder:
(BuildContext context, AsyncSnapshot<WebViewController> controller) {
return PopupMenuButton<_MenuOptions>(
key: const ValueKey<String>('ShowPopupMenu'),
onSelected: (_MenuOptions value) {
switch (value) {
case _MenuOptions.showUserAgent:
Expand Down Expand Up @@ -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) => <PopupMenuItem<_MenuOptions>>[
Expand Down Expand Up @@ -236,6 +264,11 @@ class _SampleMenu extends StatelessWidget {
value: _MenuOptions.doPostRequest,
child: Text('Post Request'),
),
const PopupMenuItem<_MenuOptions>(
key: ValueKey<String>('ShowTransparentBackgroundExample'),
value: _MenuOptions.transparentBackground,
child: Text('Transparent background example'),
),
],
);
},
Expand Down Expand Up @@ -346,6 +379,11 @@ class _SampleMenu extends StatelessWidget {
);
}

Future<void> _onTransparentBackground(
WebViewController controller, BuildContext context) async {
await controller.loadHtmlString(kTransparentBackgroundPage);
}

static Future<String> _prepareLocalFile() async {
final String tmpDir = (await getTemporaryDirectory()).path;
final File indexFile = File('$tmpDir/www/index.html');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -227,6 +228,12 @@ 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();
}
Expand Down Expand Up @@ -278,6 +285,7 @@ class _WebViewState extends State<WebView> {
_javascriptChannelRegistry.channels.keys.toSet(),
autoMediaPlaybackPolicy: widget.initialMediaPlaybackPolicy,
userAgent: widget.userAgent,
backgroundColor: widget.backgroundColor,
),
javascriptChannelRegistry: _javascriptChannelRegistry,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -34,4 +34,3 @@ flutter:
assets:
- assets/sample_audio.ogg
- assets/sample_video.mp4

Original file line number Diff line number Diff line change
Expand Up @@ -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.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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-opaque views have worse performance, so it's almost certainly worth special-casing this:

int alpha = backgroundColorInt >> 24 & 0xff;
UIColor* backgroundColor = [... alpha:(alpha / 255.0)];
if (alpha == 255) {
  _webView.backgroundColor = backgroundColor;
} else {
  [these three lines]
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to make the change, but if I don't set _webView.opaque = NO; for an opaque color too the WebView's background is white on my emulator.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's unfortunate; hopefully that means the drawing for WKWebView is doing something non-obvious then which will avoid the performance penalty.

}

_navigationDelegate = [[FLTWKNavigationDelegate alloc] initWithChannel:_channel];
_webView.UIDelegate = self;
_webView.navigationDelegate = _navigationDelegate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
pedantic: ^1.10.0