[webview_flutter_android] Adds support for use of old hybrid composition (#6063)

diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
index 7261cc1..a8e9a92 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 00d7c8c..61ec110 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 '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 @@
             );
           },
           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 @@
       },
     );
   }
+
+  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 a386cad..aaa4984 100644
--- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml
@@ -2,11 +2,11 @@
 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 0000000..63e752b
--- /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<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) {}
+}