diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 7261cc1c2cb5..a8e9a927d5a8 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -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 diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart index 00d7c8c53b7f..61ec11012881 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart @@ -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 @@ -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) { @@ -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(), + ); + } } diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index a386cad9028c..aaa4984bc8a1 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -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: diff --git a/packages/webview_flutter/webview_flutter_android/test/surface_android_test.dart b/packages/webview_flutter/webview_flutter_android/test/surface_android_test.dart new file mode 100644 index 000000000000..63e752b419e6 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/test/surface_android_test.dart @@ -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 log; + + setUpAll(() { + SystemChannels.platform_views.setMockMethodCallHandler( + (MethodCall call) async { + log.add(call); + if (call.method == 'resize') { + return { + 'width': call.arguments['width'], + 'height': call.arguments['height'], + }; + } + }, + ); + }); + + tearDownAll(() { + SystemChannels.platform_views.setMockMethodCallHandler(null); + }); + + setUp(() { + log = []; + }); + + 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.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.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 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) {} +}