Skip to content

Commit 3cdb50a

Browse files
bparrishMinesEgor
authored and
Egor
committed
[webview_flutter] Add new entrypoint that uses hybrid composition on Android (flutter#3067)
1 parent 7e2b325 commit 3cdb50a

File tree

7 files changed

+214
-28
lines changed

7 files changed

+214
-28
lines changed

packages/webview_flutter/CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
## 1.0.0 - Out of developer preview 🎉.
2+
3+
* Bumped the minimal Flutter SDK to 1.22 where platform views are out of developer preview, and
4+
performing better on iOS. Flutter 1.22 no longer requires adding the
5+
`io.flutter.embedded_views_preview` flag to `Info.plist`.
6+
7+
* Added support for Hybrid Composition on Android (see opt-in instructions in [README](https://github.com/flutter/plugins/blob/master/packages/webview_flutter/README.md#android))
8+
* Lowered the required Android API to 19 (was previously 20): [#23728](https://github.com/flutter/flutter/issues/23728).
9+
* Fixed the following issues:
10+
* 🎹 Keyboard: [#41089](https://github.com/flutter/flutter/issues/41089), [#36478](https://github.com/flutter/flutter/issues/36478), [#51254](https://github.com/flutter/flutter/issues/51254), [#50716](https://github.com/flutter/flutter/issues/50716), [#55724](https://github.com/flutter/flutter/issues/55724), [#56513](https://github.com/flutter/flutter/issues/56513), [#56515](https://github.com/flutter/flutter/issues/56515), [#61085](https://github.com/flutter/flutter/issues/61085), [#62205](https://github.com/flutter/flutter/issues/62205), [#62547](https://github.com/flutter/flutter/issues/62547), [#58943](https://github.com/flutter/flutter/issues/58943), [#56361](https://github.com/flutter/flutter/issues/56361), [#56361](https://github.com/flutter/flutter/issues/42902), [#40716](https://github.com/flutter/flutter/issues/40716), [#37989](https://github.com/flutter/flutter/issues/37989), [#27924](https://github.com/flutter/flutter/issues/27924).
11+
* ♿️ Accessibility: [#50716](https://github.com/flutter/flutter/issues/50716).
12+
* ⚡️ Performance: [#61280](https://github.com/flutter/flutter/issues/61280), [#31243](https://github.com/flutter/flutter/issues/31243), [#52211](https://github.com/flutter/flutter/issues/52211).
13+
* 📹 Video: [#5191](https://github.com/flutter/flutter/issues/5191).
14+
115
## 0.3.24
216

317
* Keep handling deprecated Android v1 classes for backward compatibility.

packages/webview_flutter/README.md

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# WebView for Flutter (Developers Preview)
1+
# WebView for Flutter
22

33
[![pub package](https://img.shields.io/pub/v/webview_flutter.svg)](https://pub.dartlang.org/packages/webview_flutter)
44

@@ -7,30 +7,58 @@ A Flutter plugin that provides a WebView widget.
77
On iOS the WebView widget is backed by a [WKWebView](https://developer.apple.com/documentation/webkit/wkwebview);
88
On Android the WebView widget is backed by a [WebView](https://developer.android.com/reference/android/webkit/WebView).
99

10-
## Developers Preview Status
11-
The plugin relies on Flutter's new mechanism for embedding Android and iOS views.
12-
As that mechanism is currently in a developers preview, this plugin should also be
13-
considered a developers preview.
10+
## Usage
11+
Add `webview_flutter` as a [dependency in your pubspec.yaml file](https://flutter.io/platform-plugins/).
1412

15-
Known issues are tagged with the [platform-views](https://github.com/flutter/flutter/labels/a%3A%20platform-views) and/or [webview](https://github.com/flutter/flutter/labels/p%3A%20webview) labels.
13+
You can now include a WebView widget in your widget tree. See the
14+
[WebView](https://pub.dev/documentation/webview_flutter/latest/webview_flutter/WebView-class.html)
15+
widget's Dartdoc for more details on how to use the widget.
1616

17-
To use this plugin on iOS you need to opt-in for the embedded views preview by
18-
adding a boolean property to the app's `Info.plist` file, with the key `io.flutter.embedded_views_preview`
19-
and the value `YES`.
2017

21-
## Keyboard support - not ready for production use
22-
Keyboard support within webviews is experimental. The Android version relies on some low-level knobs that have not been well tested
23-
on a broad spectrum of devices yet, and therefore **it is not recommended to rely on webview keyboard in production apps yet**.
24-
See the [webview-keyboard](https://github.com/flutter/flutter/issues?q=is%3Aopen+is%3Aissue+label%3A%22p%3A+webview-keyboard%22) for known issues with keyboard input.
2518

26-
## Setup
19+
## Android Platform Views
20+
The WebView is relying on
21+
[Platform Views](https://flutter.dev/docs/development/platform-integration/platform-views) to embed
22+
the Android’s webview within the Flutter app. By default a Virtual Display based platform view
23+
backend is used, this implementation has multiple
24+
[keyboard](https://github.com/flutter/flutter/issues?q=is%3Aopen+label%3Avd-only+label%3A%22p%3A+webview-keyboard%22).
25+
When keyboard input is required we recommend using the Hybrid Composition based platform views
26+
implementation. Note that on Android versions prior to Android 10 Hybrid Composition has some
27+
[performance drawbacks](https://flutter.dev/docs/development/platform-integration/platform-views#performance).
2728

28-
### iOS
29-
Opt-in to the embedded views preview by adding a boolean property to the app's `Info.plist` file
30-
with the key `io.flutter.embedded_views_preview` and the value `YES`.
29+
### Using Hybrid Composition
3130

32-
## Usage
33-
Add `webview_flutter` as a [dependency in your pubspec.yaml file](https://flutter.io/platform-plugins/).
31+
To enable hybrid composition, set `WebView.platform = SurfaceAndroidWebView();` in `initState()`.
32+
For example:
33+
34+
```dart
35+
import 'dart:io';
36+
37+
import 'package:webview_flutter/webview_flutter.dart';
38+
39+
class WebViewExample extends StatefulWidget {
40+
@override
41+
void initState() {
42+
super.initState();
43+
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
44+
}
45+
46+
@override
47+
Widget build(BuildContext context) {
48+
return WebView(
49+
initialUrl: 'https://flutter.dev',
50+
);
51+
}
52+
}
53+
```
54+
55+
`SurfaceAndroidWebView()` requires [API level 19](https://developer.android.com/studio/releases/platforms?hl=th#4.4). The plugin itself doesn't enforce the API level, so if you want to make the app available on devices running this API level or above, add the following to `<your-app>/android/app/build.gradle`:
3456

35-
You can now include a WebView widget in your widget tree.
36-
See the WebView widget's Dartdoc for more details on how to use the widget.
57+
```gradle
58+
android {
59+
defaultConfig {
60+
// Required by the Flutter WebView plugin.
61+
minSdkVersion 19
62+
}
63+
}
64+
```

packages/webview_flutter/example/integration_test/webview_flutter_test.dart

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,84 @@ void main() {
608608
});
609609
});
610610

611+
group('$SurfaceAndroidWebView', () {
612+
setUpAll(() {
613+
WebView.platform = SurfaceAndroidWebView();
614+
});
615+
616+
tearDownAll(() {
617+
WebView.platform = null;
618+
});
619+
620+
testWidgets('setAndGetScrollPosition', (WidgetTester tester) async {
621+
final String scrollTestPage = '''
622+
<!DOCTYPE html>
623+
<html>
624+
<head>
625+
<style>
626+
body {
627+
height: 100%;
628+
width: 100%;
629+
}
630+
#container{
631+
width:5000px;
632+
height:5000px;
633+
}
634+
</style>
635+
</head>
636+
<body>
637+
<div id="container"/>
638+
</body>
639+
</html>
640+
''';
641+
642+
final String scrollTestPageBase64 =
643+
base64Encode(const Utf8Encoder().convert(scrollTestPage));
644+
645+
final Completer<void> pageLoaded = Completer<void>();
646+
final Completer<WebViewController> controllerCompleter =
647+
Completer<WebViewController>();
648+
649+
await tester.pumpWidget(
650+
Directionality(
651+
textDirection: TextDirection.ltr,
652+
child: WebView(
653+
initialUrl:
654+
'data:text/html;charset=utf-8;base64,$scrollTestPageBase64',
655+
onWebViewCreated: (WebViewController controller) {
656+
controllerCompleter.complete(controller);
657+
},
658+
onPageFinished: (String url) {
659+
pageLoaded.complete(null);
660+
},
661+
),
662+
),
663+
);
664+
665+
final WebViewController controller = await controllerCompleter.future;
666+
await pageLoaded.future;
667+
668+
await tester.pumpAndSettle(Duration(seconds: 3));
669+
670+
// Check scrollTo()
671+
const int X_SCROLL = 123;
672+
const int Y_SCROLL = 321;
673+
674+
await controller.scrollTo(X_SCROLL, Y_SCROLL);
675+
int scrollPosX = await controller.getScrollX();
676+
int scrollPosY = await controller.getScrollY();
677+
expect(X_SCROLL, scrollPosX);
678+
expect(Y_SCROLL, scrollPosY);
679+
680+
// Check scrollBy() (on top of scrollTo())
681+
await controller.scrollBy(X_SCROLL, Y_SCROLL);
682+
scrollPosX = await controller.getScrollX();
683+
scrollPosY = await controller.getScrollY();
684+
expect(X_SCROLL * 2, scrollPosX);
685+
expect(Y_SCROLL * 2, scrollPosY);
686+
});
687+
}, skip: !Platform.isAndroid);
688+
611689
group('NavigationDelegate', () {
612690
final String blankPage = "<!DOCTYPE html><head></head><body></body></html>";
613691
final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,' +

packages/webview_flutter/example/ios/Runner/Info.plist

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@
3939
<string>UIInterfaceOrientationLandscapeLeft</string>
4040
<string>UIInterfaceOrientationLandscapeRight</string>
4141
</array>
42-
<key>io.flutter.embedded_views_preview</key>
43-
<true/>
4442
<key>UIViewControllerBasedStatusBarAppearance</key>
4543
<false/>
4644
</dict>

packages/webview_flutter/example/lib/main.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import 'dart:async';
88
import 'dart:convert';
9+
import 'dart:io';
910
import 'package:flutter/material.dart';
1011
import 'package:webview_flutter/webview_flutter.dart';
1112

@@ -35,6 +36,12 @@ class _WebViewExampleState extends State<WebViewExample> {
3536
final Completer<WebViewController> _controller =
3637
Completer<WebViewController>();
3738

39+
@override
40+
void initState() {
41+
super.initState();
42+
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
43+
}
44+
3845
@override
3946
Widget build(BuildContext context) {
4047
return Scaffold(

packages/webview_flutter/lib/webview_flutter.dart

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@ import 'dart:async';
66

77
import 'package:flutter/foundation.dart';
88
import 'package:flutter/gestures.dart';
9+
import 'package:flutter/rendering.dart';
10+
import 'package:flutter/services.dart';
911
import 'package:flutter/widgets.dart';
1012

1113
import 'platform_interface.dart';
1214
import 'src/webview_android.dart';
1315
import 'src/webview_cupertino.dart';
16+
import 'src/webview_method_channel.dart';
1417

1518
/// Optional callback invoked when a web view is first created. [controller] is
1619
/// the [WebViewController] for the created web view.
@@ -64,6 +67,66 @@ enum NavigationDecision {
6467
navigate,
6568
}
6669

70+
/// Android [WebViewPlatform] that uses [AndroidViewSurface] to build the [WebView] widget.
71+
///
72+
/// To use this, set [WebView.platform] to an instance of this class.
73+
///
74+
/// This implementation uses hybrid composition to render the [WebView] on
75+
/// Android. It solves multiple issues related to accessibility and interaction
76+
/// with the [WebView] at the cost of some performance on Android versions below
77+
/// 10. See https://github.com/flutter/flutter/wiki/Hybrid-Composition for more
78+
/// information.
79+
class SurfaceAndroidWebView extends AndroidWebView {
80+
@override
81+
Widget build({
82+
BuildContext context,
83+
CreationParams creationParams,
84+
WebViewPlatformCreatedCallback onWebViewPlatformCreated,
85+
Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers,
86+
@required WebViewPlatformCallbacksHandler webViewPlatformCallbacksHandler,
87+
}) {
88+
assert(webViewPlatformCallbacksHandler != null);
89+
return PlatformViewLink(
90+
viewType: 'plugins.flutter.io/webview',
91+
surfaceFactory: (
92+
BuildContext context,
93+
PlatformViewController controller,
94+
) {
95+
return AndroidViewSurface(
96+
controller: controller,
97+
gestureRecognizers: gestureRecognizers ??
98+
const <Factory<OneSequenceGestureRecognizer>>{},
99+
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
100+
);
101+
},
102+
onCreatePlatformView: (PlatformViewCreationParams params) {
103+
return PlatformViewsService.initSurfaceAndroidView(
104+
id: params.id,
105+
viewType: 'plugins.flutter.io/webview',
106+
// WebView content is not affected by the Android view's layout direction,
107+
// we explicitly set it here so that the widget doesn't require an ambient
108+
// directionality.
109+
layoutDirection: TextDirection.rtl,
110+
creationParams: MethodChannelWebViewPlatform.creationParamsToMap(
111+
creationParams,
112+
),
113+
creationParamsCodec: const StandardMessageCodec(),
114+
)
115+
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
116+
..addOnPlatformViewCreatedListener((int id) {
117+
if (onWebViewPlatformCreated == null) {
118+
return;
119+
}
120+
onWebViewPlatformCreated(
121+
MethodChannelWebViewPlatform(id, webViewPlatformCallbacksHandler),
122+
);
123+
})
124+
..create();
125+
},
126+
);
127+
}
128+
}
129+
67130
/// Decides how to handle a specific navigation request.
68131
///
69132
/// The returned [NavigationDecision] determines how the navigation described by
@@ -445,9 +508,7 @@ WebSettings _clearUnchangedWebSettings(
445508

446509
Set<String> _extractChannelNames(Set<JavascriptChannel> channels) {
447510
final Set<String> channelNames = channels == null
448-
// TODO(iskakaushik): Remove this when collection literals makes it to stable.
449-
// ignore: prefer_collection_literals
450-
? Set<String>()
511+
? <String>{}
451512
: channels.map((JavascriptChannel channel) => channel.name).toSet();
452513
return channelNames;
453514
}

packages/webview_flutter/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
name: webview_flutter
22
description: A Flutter plugin that provides a WebView widget on Android and iOS.
3-
version: 0.3.24
3+
version: 1.0.0
44
homepage: https://github.com/flutter/plugins/tree/master/packages/webview_flutter
55

66
environment:
77
sdk: ">=2.7.0 <3.0.0"
8-
flutter: ">=1.12.13+hotfix.5"
8+
flutter: ">=1.22.0 <2.0.0"
99

1010
dependencies:
1111
flutter:

0 commit comments

Comments
 (0)