diff --git a/packages/webview_flutter/CHANGELOG.md b/packages/webview_flutter/CHANGELOG.md index 5e32e4952799..f32724589020 100644 --- a/packages/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/CHANGELOG.md @@ -1,3 +1,17 @@ +## 1.0.0 - Out of developer preview 🎉. + +* Bumped the minimal Flutter SDK to 1.22 where platform views are out of developer preview, and +performing better on iOS. Flutter 1.22 no longer requires adding the +`io.flutter.embedded_views_preview` flag to `Info.plist`. + +* 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)) + * Lowered the required Android API to 19 (was previously 20): [#23728](https://github.com/flutter/flutter/issues/23728). + * Fixed the following issues: + * 🎹 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). + * ♿️ Accessibility: [#50716](https://github.com/flutter/flutter/issues/50716). + * ⚡️ 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). + * 📹 Video: [#5191](https://github.com/flutter/flutter/issues/5191). + ## 0.3.24 * Keep handling deprecated Android v1 classes for backward compatibility. diff --git a/packages/webview_flutter/README.md b/packages/webview_flutter/README.md index c86993a15a09..c7b55f1c89dd 100644 --- a/packages/webview_flutter/README.md +++ b/packages/webview_flutter/README.md @@ -1,4 +1,4 @@ -# WebView for Flutter (Developers Preview) +# WebView for Flutter [![pub package](https://img.shields.io/pub/v/webview_flutter.svg)](https://pub.dartlang.org/packages/webview_flutter) @@ -7,30 +7,58 @@ A Flutter plugin that provides a WebView widget. On iOS the WebView widget is backed by a [WKWebView](https://developer.apple.com/documentation/webkit/wkwebview); On Android the WebView widget is backed by a [WebView](https://developer.android.com/reference/android/webkit/WebView). -## Developers Preview Status -The plugin relies on Flutter's new mechanism for embedding Android and iOS views. -As that mechanism is currently in a developers preview, this plugin should also be -considered a developers preview. +## Usage +Add `webview_flutter` as a [dependency in your pubspec.yaml file](https://flutter.io/platform-plugins/). -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. +You can now include a WebView widget in your widget tree. See the +[WebView](https://pub.dev/documentation/webview_flutter/latest/webview_flutter/WebView-class.html) +widget's Dartdoc for more details on how to use the widget. -To use this plugin on iOS you need to opt-in for the embedded views preview by -adding a boolean property to the app's `Info.plist` file, with the key `io.flutter.embedded_views_preview` -and the value `YES`. -## Keyboard support - not ready for production use -Keyboard support within webviews is experimental. The Android version relies on some low-level knobs that have not been well tested -on a broad spectrum of devices yet, and therefore **it is not recommended to rely on webview keyboard in production apps yet**. -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. -## Setup +## Android Platform Views +The WebView is relying on +[Platform Views](https://flutter.dev/docs/development/platform-integration/platform-views) to embed +the Android’s webview within the Flutter app. By default a Virtual Display based platform view +backend is used, this implementation has multiple +[keyboard](https://github.com/flutter/flutter/issues?q=is%3Aopen+label%3Avd-only+label%3A%22p%3A+webview-keyboard%22). +When keyboard input is required we recommend using the Hybrid Composition based platform views +implementation. Note that on Android versions prior to Android 10 Hybrid Composition has some +[performance drawbacks](https://flutter.dev/docs/development/platform-integration/platform-views#performance). -### iOS -Opt-in to the embedded views preview by adding a boolean property to the app's `Info.plist` file -with the key `io.flutter.embedded_views_preview` and the value `YES`. +### Using Hybrid Composition -## Usage -Add `webview_flutter` as a [dependency in your pubspec.yaml file](https://flutter.io/platform-plugins/). +To enable hybrid composition, set `WebView.platform = SurfaceAndroidWebView();` in `initState()`. +For example: + +```dart +import 'dart:io'; + +import 'package:webview_flutter/webview_flutter.dart'; + +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', + ); + } +} +``` + +`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 `/android/app/build.gradle`: -You can now include a WebView widget in your widget tree. -See the WebView widget's Dartdoc for more details on how to use the widget. +```gradle +android { + defaultConfig { + // Required by the Flutter WebView plugin. + minSdkVersion 19 + } + } +``` diff --git a/packages/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/example/integration_test/webview_flutter_test.dart index 53fc991c48f2..2a17c53a4c3f 100644 --- a/packages/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -608,6 +608,84 @@ void main() { }); }); + group('$SurfaceAndroidWebView', () { + setUpAll(() { + WebView.platform = SurfaceAndroidWebView(); + }); + + tearDownAll(() { + WebView.platform = null; + }); + + testWidgets('setAndGetScrollPosition', (WidgetTester tester) async { + final String scrollTestPage = ''' + + + + + + +
+ + + '''; + + final String scrollTestPageBase64 = + base64Encode(const Utf8Encoder().convert(scrollTestPage)); + + final Completer pageLoaded = Completer(); + final Completer controllerCompleter = + Completer(); + + 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 = ""; final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,' + diff --git a/packages/webview_flutter/example/ios/Runner/Info.plist b/packages/webview_flutter/example/ios/Runner/Info.plist index 94f5857376a2..a810c5a172c0 100644 --- a/packages/webview_flutter/example/ios/Runner/Info.plist +++ b/packages/webview_flutter/example/ios/Runner/Info.plist @@ -39,8 +39,6 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - io.flutter.embedded_views_preview - UIViewControllerBasedStatusBarAppearance diff --git a/packages/webview_flutter/example/lib/main.dart b/packages/webview_flutter/example/lib/main.dart index 59c87a25dedf..ff25e8a16c9b 100644 --- a/packages/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/example/lib/main.dart @@ -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'; @@ -35,6 +36,12 @@ class _WebViewExampleState extends State { final Completer _controller = Completer(); + @override + void initState() { + super.initState(); + if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView(); + } + @override Widget build(BuildContext context) { return Scaffold( diff --git a/packages/webview_flutter/lib/webview_flutter.dart b/packages/webview_flutter/lib/webview_flutter.dart index 2635b0446fa2..5e2bffd6539d 100644 --- a/packages/webview_flutter/lib/webview_flutter.dart +++ b/packages/webview_flutter/lib/webview_flutter.dart @@ -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. @@ -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> 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 >{}, + 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 @@ -445,9 +508,7 @@ WebSettings _clearUnchangedWebSettings( Set _extractChannelNames(Set channels) { final Set channelNames = channels == null - // TODO(iskakaushik): Remove this when collection literals makes it to stable. - // ignore: prefer_collection_literals - ? Set() + ? {} : channels.map((JavascriptChannel channel) => channel.name).toSet(); return channelNames; } diff --git a/packages/webview_flutter/pubspec.yaml b/packages/webview_flutter/pubspec.yaml index 2212ef6c4059..aa9c606a5688 100644 --- a/packages/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/pubspec.yaml @@ -1,11 +1,11 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. -version: 0.3.24 +version: 1.0.0 homepage: https://github.com/flutter/plugins/tree/master/packages/webview_flutter environment: sdk: ">=2.7.0 <3.0.0" - flutter: ">=1.12.13+hotfix.5" + flutter: ">=1.22.0 <2.0.0" dependencies: flutter: