Skip to content

Commit 0d7a605

Browse files
[webview_flutter] Add new entrypoint that uses hybrid composition on Android (flutter#2883)
1 parent 2595703 commit 0d7a605

File tree

5 files changed

+174
-4
lines changed

5 files changed

+174
-4
lines changed

packages/webview_flutter/CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
## 0.3.24
2+
3+
* Add support for building `WebView` widget with Android hybrid views. To use this feature, set
4+
`WebView.platform` to an instance of `SurfaceAndroidWebView`. For example:
5+
6+
```dart
7+
import 'dart:io';
8+
9+
class WebViewExample extends StatefulWidget {
10+
@override
11+
void initState() {
12+
super.initState();
13+
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
14+
}
15+
16+
@override
17+
Widget build(BuildContext context) {
18+
return WebView(
19+
initialUrl: 'https://flutter.dev',
20+
);
21+
}
22+
}
23+
```
24+
125
## 0.3.23
226

327
* Handle WebView multi-window support.

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/example/test_driver/webview_flutter_e2e.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/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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: webview_flutter
22
description: A Flutter plugin that provides a WebView widget on Android and iOS.
3-
version: 0.3.23
3+
version: 0.3.24
44
homepage: https://github.com/flutter/plugins/tree/master/packages/webview_flutter
55

66
environment:

0 commit comments

Comments
 (0)