Skip to content

Commit

Permalink
[webview_flutter_android] Adds support for use of old hybrid composit…
Browse files Browse the repository at this point in the history
…ion (flutter#6063)
  • Loading branch information
bparrishMines authored and mauricioluz committed Jan 26, 2023
1 parent 69deb8f commit 81f6ab8
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
## NEXT
## 2.9.0

* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316).
* Fixes bug where `Directionality` from context didn't affect `SurfaceAndroidWebView`.
* Fixes bug where default text direction was different for `SurfaceAndroidWebView` and `AndroidWebView`.
Default is now `TextDirection.ltr` for both.
* Fixes bug where setting WebView to a transparent background could cause visual errors when using
`SurfaceAndroidWebView`. Hybrid composition is now used when the background color is not 100%
opaque.
* Raises minimum Flutter version to 3.0.0.

## 2.8.14

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,20 @@ import 'src/instance_manager.dart';
import 'webview_android.dart';
import 'webview_android_widget.dart';

/// Android [WebViewPlatform] that uses [AndroidViewSurface] to build the [WebView] widget.
/// Android [WebViewPlatform] that uses [AndroidViewSurface] to build the
/// [WebView] widget.
///
/// To use this, set [WebView.platform] to an instance of this class.
///
/// This implementation uses hybrid composition to render the [WebView] on
/// This implementation uses [AndroidViewSurface] to render the [WebView] on
/// Android. It solves multiple issues related to accessibility and interaction
/// with the [WebView] at the cost of some performance on Android versions below
/// 10. See https://github.com/flutter/flutter/wiki/Hybrid-Composition for more
/// 10.
///
/// To support transparent backgrounds on all Android devices, this
/// implementation uses hybrid composition when the opacity of
/// `CreationParams.backgroundColor` is less than 1.0. See
/// https://github.com/flutter/flutter/wiki/Hybrid-Composition for more
/// information.
class SurfaceAndroidWebView extends AndroidWebView {
@override
Expand Down Expand Up @@ -53,16 +59,23 @@ class SurfaceAndroidWebView extends AndroidWebView {
);
},
onCreatePlatformView: (PlatformViewCreationParams params) {
return PlatformViewsService.initSurfaceAndroidView(
final Color? backgroundColor = creationParams.backgroundColor;
return _createViewController(
// On some Android devices, transparent backgrounds can cause
// rendering issues on the non hybrid composition
// AndroidViewSurface. This switches the WebView to Hybrid
// Composition when the background color is not 100% opaque.
hybridComposition:
backgroundColor != null && backgroundColor.opacity < 1.0,
id: params.id,
viewType: 'plugins.flutter.io/webview',
// WebView content is not affected by the Android view's layout direction,
// we explicitly set it here so that the widget doesn't require an ambient
// directionality.
layoutDirection: TextDirection.rtl,
creationParams:
InstanceManager.instance.getInstanceId(controller.webView),
creationParamsCodec: const StandardMessageCodec(),
layoutDirection:
Directionality.maybeOf(context) ?? TextDirection.ltr,
webViewIdentifier:
InstanceManager.instance.getInstanceId(controller.webView)!,
)
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
..addOnPlatformViewCreatedListener((int id) {
Expand All @@ -76,4 +89,29 @@ class SurfaceAndroidWebView extends AndroidWebView {
},
);
}

AndroidViewController _createViewController({
required bool hybridComposition,
required int id,
required String viewType,
required TextDirection layoutDirection,
required int webViewIdentifier,
}) {
if (hybridComposition) {
return PlatformViewsService.initExpensiveAndroidView(
id: id,
viewType: viewType,
layoutDirection: layoutDirection,
creationParams: webViewIdentifier,
creationParamsCodec: const StandardMessageCodec(),
);
}
return PlatformViewsService.initSurfaceAndroidView(
id: id,
viewType: viewType,
layoutDirection: layoutDirection,
creationParams: webViewIdentifier,
creationParamsCodec: const StandardMessageCodec(),
);
}
}
4 changes: 2 additions & 2 deletions packages/webview_flutter/webview_flutter_android/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ name: webview_flutter_android
description: A Flutter plugin that provides a WebView widget on Android.
repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_android
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22
version: 2.8.14
version: 2.9.0

environment:
sdk: ">=2.14.0 <3.0.0"
flutter: ">=2.8.0"
flutter: ">=3.0.0"

flutter:
plugin:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// 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 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:webview_flutter_android/webview_surface_android.dart';
import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';

void main() {
TestWidgetsFlutterBinding.ensureInitialized();

group('SurfaceAndroidWebView', () {
late List<MethodCall> log;

setUpAll(() {
SystemChannels.platform_views.setMockMethodCallHandler(
(MethodCall call) async {
log.add(call);
if (call.method == 'resize') {
return <String, Object?>{
'width': call.arguments['width'],
'height': call.arguments['height'],
};
}
},
);
});

tearDownAll(() {
SystemChannels.platform_views.setMockMethodCallHandler(null);
});

setUp(() {
log = <MethodCall>[];
});

testWidgets(
'uses hybrid composition when background color is not 100% opaque',
(WidgetTester tester) async {
await tester.pumpWidget(Builder(builder: (BuildContext context) {
return SurfaceAndroidWebView().build(
context: context,
creationParams: CreationParams(
backgroundColor: Colors.transparent,
webSettings: WebSettings(
userAgent: const WebSetting<String?>.absent(),
hasNavigationDelegate: false,
)),
javascriptChannelRegistry: JavascriptChannelRegistry(null),
webViewPlatformCallbacksHandler:
TestWebViewPlatformCallbacksHandler(),
);
}));
await tester.pumpAndSettle();

final MethodCall createMethodCall = log[0];
expect(createMethodCall.method, 'create');
expect(createMethodCall.arguments, containsPair('hybrid', true));
});

testWidgets('default text direction is ltr', (WidgetTester tester) async {
await tester.pumpWidget(Builder(builder: (BuildContext context) {
return SurfaceAndroidWebView().build(
context: context,
creationParams: CreationParams(
webSettings: WebSettings(
userAgent: const WebSetting<String?>.absent(),
hasNavigationDelegate: false,
)),
javascriptChannelRegistry: JavascriptChannelRegistry(null),
webViewPlatformCallbacksHandler:
TestWebViewPlatformCallbacksHandler(),
);
}));
await tester.pumpAndSettle();

final MethodCall createMethodCall = log[0];
expect(createMethodCall.method, 'create');
expect(
createMethodCall.arguments,
containsPair(
'direction',
AndroidViewController.kAndroidLayoutDirectionLtr,
),
);
});
});
}

class TestWebViewPlatformCallbacksHandler
implements WebViewPlatformCallbacksHandler {
@override
FutureOr<bool> onNavigationRequest({
required String url,
required bool isForMainFrame,
}) {
throw UnimplementedError();
}

@override
void onPageFinished(String url) {}

@override
void onPageStarted(String url) {}

@override
void onProgress(int progress) {}

@override
void onWebResourceError(WebResourceError error) {}
}

0 comments on commit 81f6ab8

Please sign in to comment.