Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Commit

Permalink
[webview_flutter] Add new entrypoint that uses hybrid composition on …
Browse files Browse the repository at this point in the history
…Android (#2883)
  • Loading branch information
bparrishMines committed Sep 21, 2020
1 parent 2595703 commit 0d7a605
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 4 deletions.
24 changes: 24 additions & 0 deletions packages/webview_flutter/CHANGELOG.md
@@ -1,3 +1,27 @@
## 0.3.24

* Add support for building `WebView` widget with Android hybrid views. To use this feature, set
`WebView.platform` to an instance of `SurfaceAndroidWebView`. For example:

```dart
import 'dart:io';
class WebViewExample extends StatefulWidget {
@override
void initState() {
super.initState();
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
}
@override
Widget build(BuildContext context) {
return WebView(
initialUrl: 'https://flutter.dev',
);
}
}
```

## 0.3.23

* Handle WebView multi-window support.
Expand Down
7 changes: 7 additions & 0 deletions packages/webview_flutter/example/lib/main.dart
Expand Up @@ -6,6 +6,7 @@

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

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

@override
void initState() {
super.initState();
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
}

@override
Widget build(BuildContext context) {
return Scaffold(
Expand Down
Expand Up @@ -608,6 +608,84 @@ void main() {
});
});

group('$SurfaceAndroidWebView', () {
setUpAll(() {
WebView.platform = SurfaceAndroidWebView();
});

tearDownAll(() {
WebView.platform = null;
});

testWidgets('setAndGetScrollPosition', (WidgetTester tester) async {
final String scrollTestPage = '''
<!DOCTYPE html>
<html>
<head>
<style>
body {
height: 100%;
width: 100%;
}
#container{
width:5000px;
height:5000px;
}
</style>
</head>
<body>
<div id="container"/>
</body>
</html>
''';

final String scrollTestPageBase64 =
base64Encode(const Utf8Encoder().convert(scrollTestPage));

final Completer<void> pageLoaded = Completer<void>();
final Completer<WebViewController> controllerCompleter =
Completer<WebViewController>();

await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: WebView(
initialUrl:
'data:text/html;charset=utf-8;base64,$scrollTestPageBase64',
onWebViewCreated: (WebViewController controller) {
controllerCompleter.complete(controller);
},
onPageFinished: (String url) {
pageLoaded.complete(null);
},
),
),
);

final WebViewController controller = await controllerCompleter.future;
await pageLoaded.future;

await tester.pumpAndSettle(Duration(seconds: 3));

// Check scrollTo()
const int X_SCROLL = 123;
const int Y_SCROLL = 321;

await controller.scrollTo(X_SCROLL, Y_SCROLL);
int scrollPosX = await controller.getScrollX();
int scrollPosY = await controller.getScrollY();
expect(X_SCROLL, scrollPosX);
expect(Y_SCROLL, scrollPosY);

// Check scrollBy() (on top of scrollTo())
await controller.scrollBy(X_SCROLL, Y_SCROLL);
scrollPosX = await controller.getScrollX();
scrollPosY = await controller.getScrollY();
expect(X_SCROLL * 2, scrollPosX);
expect(Y_SCROLL * 2, scrollPosY);
});
}, skip: !Platform.isAndroid);

group('NavigationDelegate', () {
final String blankPage = "<!DOCTYPE html><head></head><body></body></html>";
final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,' +
Expand Down
67 changes: 64 additions & 3 deletions packages/webview_flutter/lib/webview_flutter.dart
Expand Up @@ -6,11 +6,14 @@ import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';

import 'platform_interface.dart';
import 'src/webview_android.dart';
import 'src/webview_cupertino.dart';
import 'src/webview_method_channel.dart';

/// Optional callback invoked when a web view is first created. [controller] is
/// the [WebViewController] for the created web view.
Expand Down Expand Up @@ -64,6 +67,66 @@ enum NavigationDecision {
navigate,
}

/// 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
/// 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
/// information.
class SurfaceAndroidWebView extends AndroidWebView {
@override
Widget build({
BuildContext context,
CreationParams creationParams,
WebViewPlatformCreatedCallback onWebViewPlatformCreated,
Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers,
@required WebViewPlatformCallbacksHandler webViewPlatformCallbacksHandler,
}) {
assert(webViewPlatformCallbacksHandler != null);
return PlatformViewLink(
viewType: 'plugins.flutter.io/webview',
surfaceFactory: (
BuildContext context,
PlatformViewController controller,
) {
return AndroidViewSurface(
controller: controller,
gestureRecognizers: gestureRecognizers ??
const <Factory<OneSequenceGestureRecognizer>>{},
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
);
},
onCreatePlatformView: (PlatformViewCreationParams params) {
return PlatformViewsService.initSurfaceAndroidView(
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: MethodChannelWebViewPlatform.creationParamsToMap(
creationParams,
),
creationParamsCodec: const StandardMessageCodec(),
)
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
..addOnPlatformViewCreatedListener((int id) {
if (onWebViewPlatformCreated == null) {
return;
}
onWebViewPlatformCreated(
MethodChannelWebViewPlatform(id, webViewPlatformCallbacksHandler),
);
})
..create();
},
);
}
}

/// Decides how to handle a specific navigation request.
///
/// The returned [NavigationDecision] determines how the navigation described by
Expand Down Expand Up @@ -445,9 +508,7 @@ WebSettings _clearUnchangedWebSettings(

Set<String> _extractChannelNames(Set<JavascriptChannel> channels) {
final Set<String> channelNames = channels == null
// TODO(iskakaushik): Remove this when collection literals makes it to stable.
// ignore: prefer_collection_literals
? Set<String>()
? <String>{}
: channels.map((JavascriptChannel channel) => channel.name).toSet();
return channelNames;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/webview_flutter/pubspec.yaml
@@ -1,6 +1,6 @@
name: webview_flutter
description: A Flutter plugin that provides a WebView widget on Android and iOS.
version: 0.3.23
version: 0.3.24
homepage: https://github.com/flutter/plugins/tree/master/packages/webview_flutter

environment:
Expand Down

0 comments on commit 0d7a605

Please sign in to comment.