Skip to content

Commit

Permalink
[webview_flutter] Add a backgroundColor option to the Android webview (
Browse files Browse the repository at this point in the history
…flutter#4569)

This PR add an option to set the background color of the Android webview.

Part of: flutter#3431
Part of: flutter/flutter#29300
  • Loading branch information
e-adrien authored and amantoux committed Dec 11, 2021
1 parent 73cf5e6 commit 6a5f10b
Show file tree
Hide file tree
Showing 17 changed files with 275 additions and 14 deletions.
11 changes: 7 additions & 4 deletions packages/webview_flutter/webview_flutter_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.5.0

* Adds an option to set the background color of the webview.

## 2.4.0

* Adds support for Android's `WebView.loadData` and `WebView.loadDataWithBaseUrl` methods and implements the `loadFile` and `loadHtmlString` methods from the platform interface.
Expand Down Expand Up @@ -27,13 +31,12 @@ when it is created without Hybrid Composition.

## 2.0.15

* Added Overrides in FlutterWebView.java
* Added Overrides in FlutterWebView.java

## 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

* Extract Android implementation from `webview_flutter`.

Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ void loadDataWithBaseUrl(

void setWebChromeClient(Long instanceId, Long clientInstanceId);

void setBackgroundColor(Long instanceId, Long color);

/** The codec used by WebViewHostApi. */
static MessageCodec<Object> getCodec() {
return WebViewHostApiCodec.INSTANCE;
Expand Down Expand Up @@ -958,6 +960,37 @@ public void error(Throwable error) {
channel.setMessageHandler(null);
}
}
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.WebViewHostApi.setBackgroundColor",
getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
Map<String, Object> wrapped = new HashMap<>();
try {
ArrayList<Object> args = (ArrayList<Object>) message;
Number instanceIdArg = (Number) args.get(0);
if (instanceIdArg == null) {
throw new NullPointerException("instanceIdArg unexpectedly null.");
}
Number colorArg = (Number) args.get(1);
if (colorArg == null) {
throw new NullPointerException("colorArg unexpectedly null.");
}
api.setBackgroundColor(instanceIdArg.longValue(), colorArg.longValue());
wrapped.put("result", null);
} catch (Error | RuntimeException exception) {
wrapped.put("error", wrapError(exception));
}
reply.reply(wrapped);
});
} else {
channel.setMessageHandler(null);
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,12 @@ public void setWebChromeClient(Long instanceId, Long clientInstanceId) {
webView.setWebChromeClient((WebChromeClient) instanceManager.getInstance(clientInstanceId));
}

@Override
public void setBackgroundColor(Long instanceId, Long color) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
webView.setBackgroundColor(color.intValue());
}

@Nullable
private static String parseNullStringIdentifier(String value) {
if (value.equals(nullStringIdentifier)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// 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.

package io.flutter.plugins.webviewflutterexample;

import static androidx.test.espresso.flutter.EspressoFlutter.onFlutterWidget;
import static androidx.test.espresso.flutter.action.FlutterActions.click;
import static androidx.test.espresso.flutter.matcher.FlutterMatchers.withText;
import static androidx.test.espresso.flutter.matcher.FlutterMatchers.withValueKey;
import static org.junit.Assert.assertEquals;

import android.graphics.Bitmap;
import android.graphics.Color;
import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.screenshot.ScreenCapture;
import androidx.test.runner.screenshot.Screenshot;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
public class BackgroundColorTest {
@Rule
public ActivityTestRule<DriverExtensionActivity> myActivityTestRule =
new ActivityTestRule<>(DriverExtensionActivity.class, true, false);

@Before
public void setUp() {
ActivityScenario.launch(DriverExtensionActivity.class);
}

@Ignore("Doesn't run in Firebase Test Lab: https://github.com/flutter/flutter/issues/94748")
@Test
public void backgroundColor() {
onFlutterWidget(withValueKey("ShowPopupMenu")).perform(click());
onFlutterWidget(withValueKey("ShowTransparentBackgroundExample")).perform(click());
onFlutterWidget(withText("Transparent background test"));

final ScreenCapture screenCapture = Screenshot.capture();
final Bitmap screenBitmap = screenCapture.getBitmap();

final int centerLeftColor =
screenBitmap.getPixel(10, (int) Math.floor(screenBitmap.getHeight() / 2.0));
final int centerColor =
screenBitmap.getPixel(
(int) Math.floor(screenBitmap.getWidth() / 2.0),
(int) Math.floor(screenBitmap.getHeight() / 2.0));

// Flutter Colors.green color : 0xFF4CAF50
// 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) -> 0xFF265728
assertEquals(0xFF265728, centerLeftColor);
assertEquals(Color.RED, centerColor);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,13 @@
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
</activity>
<activity
android:name=".DriverExtensionActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
</activity>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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.

package io.flutter.plugins.webviewflutterexample;

import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity;

public class DriverExtensionActivity extends FlutterActivity {
@Override
@NonNull
public String getDartEntrypointFunctionName() {
return "appMain";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_driver/driver_extension.dart';
import 'package:path_provider/path_provider.dart';
import 'package:webview_flutter_android/webview_surface_android.dart';
import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
Expand All @@ -17,6 +18,11 @@ import 'navigation_decision.dart';
import 'navigation_request.dart';
import 'web_view.dart';

void appMain() {
enableFlutterDriverExtension();
main();
}

void main() {
// Configure the [WebView] to use the [SurfaceAndroidWebView]
// implementation instead of the default [AndroidWebView].
Expand Down Expand Up @@ -59,6 +65,27 @@ const String kExamplePage = '''
</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>
''';

class _WebViewExample extends StatefulWidget {
const _WebViewExample({Key? key}) : super(key: key);

Expand All @@ -73,6 +100,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 Down Expand Up @@ -109,6 +137,7 @@ class _WebViewExampleState extends State<_WebViewExample> {
javascriptChannels: _createJavascriptChannels(context),
javascriptMode: JavascriptMode.unrestricted,
userAgent: 'Custom_User_Agent',
backgroundColor: const Color(0x80000000),
);
}),
floatingActionButton: favoriteButton(),
Expand Down Expand Up @@ -158,6 +187,7 @@ enum _MenuOptions {
navigationDelegate,
loadLocalFile,
loadHtmlString,
transparentBackground,
}

class _SampleMenu extends StatelessWidget {
Expand All @@ -172,6 +202,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 @@ -201,6 +232,9 @@ class _SampleMenu extends StatelessWidget {
case _MenuOptions.loadHtmlString:
_onLoadHtmlStringExample(controller.data!, context);
break;
case _MenuOptions.transparentBackground:
_onTransparentBackground(controller.data!, context);
break;
}
},
itemBuilder: (BuildContext context) => <PopupMenuItem<_MenuOptions>>[
Expand Down Expand Up @@ -241,6 +275,11 @@ class _SampleMenu extends StatelessWidget {
value: _MenuOptions.loadLocalFile,
child: Text('Load local file'),
),
const PopupMenuItem<_MenuOptions>(
key: ValueKey<String>('ShowTransparentBackgroundExample'),
value: _MenuOptions.transparentBackground,
child: Text('Transparent background example'),
),
],
);
},
Expand Down Expand Up @@ -353,6 +392,11 @@ class _SampleMenu extends StatelessWidget {

return indexFile.path;
}

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

class _NavigationControls extends StatelessWidget {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,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 @@ -236,6 +237,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 @@ -287,6 +294,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 @@ -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';

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart' show AndroidViewSurface;

Expand Down Expand Up @@ -349,6 +351,11 @@ class WebView {
return api.setWebChromeClientFromInstance(this, client);
}

/// Sets the background color of this WebView.
Future<void> setBackgroundColor(Color color) {
return api.setBackgroundColorFromInstance(this, color.value);
}

/// Releases all resources used by the [WebView].
///
/// Any methods called after [release] will throw an exception.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,31 @@ class WebViewHostApi {
return;
}
}

Future<void> setBackgroundColor(int arg_instanceId, int arg_color) async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.WebViewHostApi.setBackgroundColor', codec,
binaryMessenger: _binaryMessenger);
final Map<Object?, Object?>? replyMap = await channel
.send(<Object>[arg_instanceId, arg_color]) as Map<Object?, Object?>?;
if (replyMap == null) {
throw PlatformException(
code: 'channel-error',
message: 'Unable to establish connection on channel.',
details: null,
);
} else if (replyMap['error'] != null) {
final Map<Object?, Object?> error =
(replyMap['error'] as Map<Object?, Object?>?)!;
throw PlatformException(
code: (error['code'] as String?)!,
message: error['message'] as String?,
details: error['details'],
);
} else {
return;
}
}
}

class _WebSettingsHostApiCodec extends StandardMessageCodec {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,11 @@ class WebViewHostApiImpl extends WebViewHostApi {
instanceManager.getInstanceId(client)!,
);
}

/// Helper method to convert instances ids to objects.
Future<void> setBackgroundColorFromInstance(WebView instance, int color) {
return setBackgroundColor(instanceManager.getInstanceId(instance)!, color);
}
}

/// Host api implementation for [WebSettings].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,11 @@ class WebViewAndroidPlatformController extends WebViewPlatformController {
AutoMediaPlaybackPolicy.always_allow,
);

final Color? backgroundColor = creationParams.backgroundColor;
if (backgroundColor != null) {
webView.setBackgroundColor(backgroundColor);
}

addJavascriptChannels(creationParams.javascriptChannelNames);
}

Expand Down
Loading

0 comments on commit 6a5f10b

Please sign in to comment.