[flutter_webview] Migrate to the new embedding (#2169)
This depends on a new bugfix on the engine for text input to work with
the new embedding (flutter/engine#13015).
diff --git a/packages/webview_flutter/CHANGELOG.md b/packages/webview_flutter/CHANGELOG.md
index 9822ad5..7710f74 100644
--- a/packages/webview_flutter/CHANGELOG.md
+++ b/packages/webview_flutter/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 0.3.15
+
+* Add support for the v2 Android embedding. This shouldn't affect existing
+ functionality. Plugin authors who use the V2 embedding can now register the
+ plugin and expect that it correctly responds to app lifecycle changes.
+
## 0.3.14+2
* Define clang module for iOS.
@@ -13,7 +19,7 @@
## 0.3.13
* Add an optional `userAgent` property to set a custom User Agent.
-
+
## 0.3.12+1
* Temporarily revert getTitle (doing this as a patch bump shortly after publishing).
diff --git a/packages/webview_flutter/android/build.gradle b/packages/webview_flutter/android/build.gradle
index 4fe7629..0104ede 100644
--- a/packages/webview_flutter/android/build.gradle
+++ b/packages/webview_flutter/android/build.gradle
@@ -50,3 +50,28 @@
implementation 'androidx.webkit:webkit:1.0.0'
}
}
+
+// TODO(mklim): Remove this hack once androidx.lifecycle is included on stable. https://github.com/flutter/flutter/issues/42348
+afterEvaluate {
+ def containsEmbeddingDependencies = false
+ for (def configuration : configurations.all) {
+ for (def dependency : configuration.dependencies) {
+ if (dependency.group == 'io.flutter' &&
+ dependency.name.startsWith('flutter_embedding') &&
+ dependency.isTransitive())
+ {
+ containsEmbeddingDependencies = true
+ break
+ }
+ }
+ }
+ if (!containsEmbeddingDependencies) {
+ android {
+ dependencies {
+ def lifecycle_version = "2.1.0"
+ api "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
+ api "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/webview_flutter/android/gradle.properties b/packages/webview_flutter/android/gradle.properties
index 8bd86f6..08f2b5f 100644
--- a/packages/webview_flutter/android/gradle.properties
+++ b/packages/webview_flutter/android/gradle.properties
@@ -1 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
+android.enableJetifier=true
+android.useAndroidX=true
diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterCookieManager.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterCookieManager.java
index 908f877..86b4fd4 100644
--- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterCookieManager.java
+++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterCookieManager.java
@@ -15,17 +15,11 @@
import io.flutter.plugin.common.MethodChannel.Result;
class FlutterCookieManager implements MethodCallHandler {
+ private final MethodChannel methodChannel;
- private FlutterCookieManager() {
- // Do not instantiate.
- // This class should only be used in context of a BinaryMessenger.
- // Use FlutterCookieManager#registerWith instead.
- }
-
- static void registerWith(BinaryMessenger messenger) {
- MethodChannel methodChannel = new MethodChannel(messenger, "plugins.flutter.io/cookie_manager");
- FlutterCookieManager cookieManager = new FlutterCookieManager();
- methodChannel.setMethodCallHandler(cookieManager);
+ FlutterCookieManager(BinaryMessenger messenger) {
+ methodChannel = new MethodChannel(messenger, "plugins.flutter.io/cookie_manager");
+ methodChannel.setMethodCallHandler(this);
}
@Override
@@ -39,6 +33,10 @@
}
}
+ void dispose() {
+ methodChannel.setMethodCallHandler(null);
+ }
+
private static void clearCookies(final Result result) {
CookieManager cookieManager = CookieManager.getInstance();
final boolean hasCookies = cookieManager.hasCookies();
diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java
index a7f2db3..ac326ed 100644
--- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java
+++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java
@@ -12,6 +12,8 @@
import android.view.View;
import android.webkit.WebStorage;
import android.webkit.WebViewClient;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
@@ -36,7 +38,7 @@
BinaryMessenger messenger,
int id,
Map<String, Object> params,
- final View containerView) {
+ @Nullable View containerView) {
DisplayListenerProxy displayListenerProxy = new DisplayListenerProxy();
DisplayManager displayManager =
@@ -96,6 +98,16 @@
}
@Override
+ public void onFlutterViewAttached(@NonNull View flutterView) {
+ webView.setContainerView(flutterView);
+ }
+
+ @Override
+ public void onFlutterViewDetached() {
+ webView.setContainerView(null);
+ }
+
+ @Override
public void onMethodCall(MethodCall methodCall, Result result) {
switch (methodCall.method) {
case "loadUrl":
diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java
index 9275c38..e04d566 100644
--- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java
+++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java
@@ -7,9 +7,11 @@
import static android.content.Context.INPUT_METHOD_SERVICE;
import android.content.Context;
+import android.util.Log;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.webkit.WebView;
+import androidx.annotation.Nullable;
/**
* A WebView subclass that mirrors the same implementation hacks that the system WebView does in
@@ -22,16 +24,29 @@
* <p>See also {@link ThreadedInputConnectionProxyAdapterView}.
*/
final class InputAwareWebView extends WebView {
- private final View containerView;
-
+ private static final String TAG = "InputAwareWebView";
private View threadedInputConnectionProxyView;
private ThreadedInputConnectionProxyAdapterView proxyAdapterView;
+ private @Nullable View containerView;
- InputAwareWebView(Context context, View containerView) {
+ InputAwareWebView(Context context, @Nullable View containerView) {
super(context);
this.containerView = containerView;
}
+ void setContainerView(@Nullable View containerView) {
+ this.containerView = containerView;
+
+ if (proxyAdapterView == null) {
+ return;
+ }
+
+ Log.w(TAG, "The containerView has changed while the proxyAdapterView exists.");
+ if (containerView != null) {
+ setInputConnectionTarget(proxyAdapterView);
+ }
+ }
+
/**
* Set our proxy adapter view to use its cached input connection instead of creating new ones.
*
@@ -81,6 +96,12 @@
// This isn't a new ThreadedInputConnectionProxyView. Ignore it.
return super.checkInputConnectionProxy(view);
}
+ if (containerView == null) {
+ Log.e(
+ TAG,
+ "Can't create a proxy view because there's no container view. Text input may not work.");
+ return super.checkInputConnectionProxy(view);
+ }
// We've never seen this before, so we make the assumption that this is WebView's
// ThreadedInputConnectionProxyView. We are making the assumption that the only view that could
@@ -120,6 +141,10 @@
// No need to reset the InputConnection to the default thread if we've never changed it.
return;
}
+ if (containerView == null) {
+ Log.e(TAG, "Can't reset the input connection to the container view because there is none.");
+ return;
+ }
setInputConnectionTarget(/*targetView=*/ containerView);
}
@@ -132,6 +157,13 @@
* InputConnections should be created on.
*/
private void setInputConnectionTarget(final View targetView) {
+ if (containerView == null) {
+ Log.e(
+ TAG,
+ "Can't set the input connection target because there is no containerView to use as a handler.");
+ return;
+ }
+
targetView.requestFocus();
containerView.post(
new Runnable() {
diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFactory.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFactory.java
index 6fdc36f..fe62e3a 100644
--- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFactory.java
+++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFactory.java
@@ -6,6 +6,7 @@
import android.content.Context;
import android.view.View;
+import androidx.annotation.Nullable;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.StandardMessageCodec;
import io.flutter.plugin.platform.PlatformView;
@@ -14,9 +15,9 @@
public final class WebViewFactory extends PlatformViewFactory {
private final BinaryMessenger messenger;
- private final View containerView;
+ private @Nullable final View containerView;
- WebViewFactory(BinaryMessenger messenger, View containerView) {
+ WebViewFactory(BinaryMessenger messenger, @Nullable View containerView) {
super(StandardMessageCodec.INSTANCE);
this.messenger = messenger;
this.containerView = containerView;
diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java
index 1717754..dcce796 100644
--- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java
+++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java
@@ -4,17 +4,68 @@
package io.flutter.plugins.webviewflutter;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import io.flutter.embedding.engine.plugins.FlutterPlugin;
+import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.PluginRegistry.Registrar;
-/** WebViewFlutterPlugin */
-public class WebViewFlutterPlugin {
- /** Plugin registration. */
+/**
+ * Java platform implementation of the webview_flutter plugin.
+ *
+ * <p>Register this in an add to app scenario to gracefully handle activity and context changes.
+ *
+ * <p>Call {@link #registerWith(Registrar)} to use the stable {@code io.flutter.plugin.common}
+ * package instead.
+ */
+public class WebViewFlutterPlugin implements FlutterPlugin {
+
+ private @Nullable FlutterCookieManager flutterCookieManager;
+
+ /**
+ * Add an instance of this to {@link io.flutter.embedding.engine.plugins.PluginRegistry} to
+ * register it.
+ *
+ * <p>Registration should eventually be handled automatically by v2 of the
+ * GeneratedPluginRegistrant. https://github.com/flutter/flutter/issues/42694
+ */
+ public WebViewFlutterPlugin() {}
+
+ /**
+ * Registers a plugin implementation that uses the stable {@code io.flutter.plugin.common}
+ * package.
+ *
+ * <p>Calling this automatically initializes the plugin. However plugins initialized this way
+ * won't react to changes in activity or context, unlike {@link CameraPlugin}.
+ */
public static void registerWith(Registrar registrar) {
registrar
.platformViewRegistry()
.registerViewFactory(
"plugins.flutter.io/webview",
new WebViewFactory(registrar.messenger(), registrar.view()));
- FlutterCookieManager.registerWith(registrar.messenger());
+ new FlutterCookieManager(registrar.messenger());
+ }
+
+ @Override
+ public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
+ BinaryMessenger messenger = binding.getFlutterEngine().getDartExecutor();
+ binding
+ .getFlutterEngine()
+ .getPlatformViewsController()
+ .getRegistry()
+ .registerViewFactory(
+ "plugins.flutter.io/webview", new WebViewFactory(messenger, /*containerView=*/ null));
+ flutterCookieManager = new FlutterCookieManager(messenger);
+ }
+
+ @Override
+ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
+ if (flutterCookieManager == null) {
+ return;
+ }
+
+ flutterCookieManager.dispose();
+ flutterCookieManager = null;
}
}
diff --git a/packages/webview_flutter/example/android/app/build.gradle b/packages/webview_flutter/example/android/app/build.gradle
index 79a69ac..706d501 100644
--- a/packages/webview_flutter/example/android/app/build.gradle
+++ b/packages/webview_flutter/example/android/app/build.gradle
@@ -56,6 +56,7 @@
dependencies {
testImplementation 'junit:junit:4.12'
- androidTestImplementation 'androidx.test:runner:1.1.1'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
+ androidTestImplementation 'androidx.test:runner:1.2.0'
+ androidTestImplementation 'androidx.test:rules:1.2.0'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
diff --git a/packages/webview_flutter/example/android/app/src/androidTestDebug/java/io/flutter/plugins/webviewflutterexample/EmbeddingV1ActivityTest.java b/packages/webview_flutter/example/android/app/src/androidTestDebug/java/io/flutter/plugins/webviewflutterexample/EmbeddingV1ActivityTest.java
new file mode 100644
index 0000000..fe10c61
--- /dev/null
+++ b/packages/webview_flutter/example/android/app/src/androidTestDebug/java/io/flutter/plugins/webviewflutterexample/EmbeddingV1ActivityTest.java
@@ -0,0 +1,13 @@
+package io.flutter.plugins.webviewflutterexample;
+
+import androidx.test.rule.ActivityTestRule;
+import dev.flutter.plugins.e2e.FlutterRunner;
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+
+@RunWith(FlutterRunner.class)
+public class EmbeddingV1ActivityTest {
+ @Rule
+ public ActivityTestRule<EmbeddingV1Activity> rule =
+ new ActivityTestRule<>(EmbeddingV1Activity.class);
+}
diff --git a/packages/webview_flutter/example/android/app/src/androidTestDebug/java/io/flutter/plugins/webviewflutterexample/MainActivityTest.java b/packages/webview_flutter/example/android/app/src/androidTestDebug/java/io/flutter/plugins/webviewflutterexample/MainActivityTest.java
new file mode 100644
index 0000000..a0bd4fe
--- /dev/null
+++ b/packages/webview_flutter/example/android/app/src/androidTestDebug/java/io/flutter/plugins/webviewflutterexample/MainActivityTest.java
@@ -0,0 +1,11 @@
+package io.flutter.plugins.webviewflutterexample;
+
+import androidx.test.rule.ActivityTestRule;
+import dev.flutter.plugins.e2e.FlutterRunner;
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+
+@RunWith(FlutterRunner.class)
+public class MainActivityTest {
+ @Rule public ActivityTestRule<MainActivity> rule = new ActivityTestRule<>(MainActivity.class);
+}
diff --git a/packages/webview_flutter/example/android/app/src/main/AndroidManifest.xml b/packages/webview_flutter/example/android/app/src/main/AndroidManifest.xml
index 8fcbcd3..fd570ac 100644
--- a/packages/webview_flutter/example/android/app/src/main/AndroidManifest.xml
+++ b/packages/webview_flutter/example/android/app/src/main/AndroidManifest.xml
@@ -1,39 +1,48 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="io.flutter.plugins.webviewflutterexample">
+ package="io.flutter.plugins.webviewflutterexample">
- <!-- The INTERNET permission is required for development. Specifically,
- flutter needs it to communicate with the running application
- to allow setting breakpoints, to provide hot reload, etc.
- -->
- <uses-permission android:name="android.permission.INTERNET"/>
+ <!-- io.flutter.app.FlutterApplication is an android.app.Application that
+ calls FlutterMain.startInitialization(this); in its onCreate method.
+ In most cases you can leave this as-is, but you if you want to provide
+ additional functionality it is fine to subclass or reimplement
+ FlutterApplication and put your custom class here. -->
+ <application
+ android:icon="@mipmap/ic_launcher"
+ android:label="webview_flutter_example"
+ android:name="io.flutter.app.FlutterApplication">
+ <activity
+ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
+ android:exported="true"
+ android:hardwareAccelerated="true"
+ android:launchMode="singleTop"
+ android:name=".EmbeddingV1Activity"
+ android:theme="@style/LaunchTheme"
+ android:windowSoftInputMode="adjustResize">
+ <!-- This keeps the window background of the activity showing
+ until Flutter renders its first frame. It can be removed if
+ there is no splash screen (such as the default splash screen
+ defined in @style/LaunchTheme). -->
+ <meta-data
+ android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
+ android:value="true"/>
+ </activity>
+ <activity
+ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
+ android:hardwareAccelerated="true"
+ android:launchMode="singleTop"
+ android:name=".MainActivity"
+ android:theme="@style/LaunchTheme"
+ android:windowSoftInputMode="adjustResize">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
- <!-- io.flutter.app.FlutterApplication is an android.app.Application that
- calls FlutterMain.startInitialization(this); in its onCreate method.
- In most cases you can leave this as-is, but you if you want to provide
- additional functionality it is fine to subclass or reimplement
- FlutterApplication and put your custom class here. -->
- <application
- android:name="io.flutter.app.FlutterApplication"
- android:label="webview_flutter_example"
- android:icon="@mipmap/ic_launcher">
- <activity
- android:name=".MainActivity"
- android:launchMode="singleTop"
- android:theme="@style/LaunchTheme"
- android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
- android:hardwareAccelerated="true"
- android:windowSoftInputMode="adjustResize">
- <!-- This keeps the window background of the activity showing
- until Flutter renders its first frame. It can be removed if
- there is no splash screen (such as the default splash screen
- defined in @style/LaunchTheme). -->
- <meta-data
- android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
- android:value="true" />
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
- </application>
+ <!-- The INTERNET permission is required for development. Specifically,
+ flutter needs it to communicate with the running application
+ to allow setting breakpoints, to provide hot reload, etc.
+ -->
+ <uses-permission android:name="android.permission.INTERNET"/>
</manifest>
diff --git a/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/EmbeddingV1Activity.java b/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/EmbeddingV1Activity.java
new file mode 100644
index 0000000..9b86893
--- /dev/null
+++ b/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/EmbeddingV1Activity.java
@@ -0,0 +1,17 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package io.flutter.plugins.webviewflutterexample;
+
+import android.os.Bundle;
+import io.flutter.app.FlutterActivity;
+import io.flutter.plugins.GeneratedPluginRegistrant;
+
+public class EmbeddingV1Activity extends FlutterActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ GeneratedPluginRegistrant.registerWith(this);
+ }
+}
diff --git a/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/MainActivity.java b/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/MainActivity.java
index f935d00..1596844 100644
--- a/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/MainActivity.java
+++ b/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/MainActivity.java
@@ -4,14 +4,14 @@
package io.flutter.plugins.webviewflutterexample;
-import android.os.Bundle;
-import io.flutter.app.FlutterActivity;
-import io.flutter.plugins.GeneratedPluginRegistrant;
+import io.flutter.embedding.android.FlutterActivity;
+import io.flutter.embedding.engine.FlutterEngine;
+import io.flutter.plugins.webviewflutter.WebViewFlutterPlugin;
public class MainActivity extends FlutterActivity {
+ // TODO(mklim): Remove this once v2 of GeneratedPluginRegistrant rolls to stable. https://github.com/flutter/flutter/issues/42694
@Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- GeneratedPluginRegistrant.registerWith(this);
+ public void configureFlutterEngine(FlutterEngine flutterEngine) {
+ flutterEngine.getPlugins().add(new WebViewFlutterPlugin());
}
}
diff --git a/packages/webview_flutter/example/android/gradle.properties b/packages/webview_flutter/example/android/gradle.properties
index ad8917e..a673820 100644
--- a/packages/webview_flutter/example/android/gradle.properties
+++ b/packages/webview_flutter/example/android/gradle.properties
@@ -1,2 +1,4 @@
org.gradle.jvmargs=-Xmx1536M
-android.useAndroidX=true
\ No newline at end of file
+android.useAndroidX=true
+android.enableJetifier=true
+android.enableR8=true
diff --git a/packages/webview_flutter/example/pubspec.yaml b/packages/webview_flutter/example/pubspec.yaml
index 8657cfd..0e24333 100644
--- a/packages/webview_flutter/example/pubspec.yaml
+++ b/packages/webview_flutter/example/pubspec.yaml
@@ -1,10 +1,11 @@
name: webview_flutter_example
description: Demonstrates how to use the webview_flutter plugin.
-version: 1.0.1
+version: 1.0.2
environment:
sdk: ">=2.0.0-dev.68.0 <3.0.0"
+ flutter: ">=1.9.1+hotfix.4 <2.0.0"
dependencies:
flutter:
@@ -17,6 +18,7 @@
sdk: flutter
flutter_driver:
sdk: flutter
+ e2e: "^0.2.0"
flutter:
uses-material-design: true
diff --git a/packages/webview_flutter/example/test_driver/webview.dart b/packages/webview_flutter/example/test_driver/webview_flutter_e2e.dart
similarity index 92%
rename from packages/webview_flutter/example/test_driver/webview.dart
rename to packages/webview_flutter/example/test_driver/webview_flutter_e2e.dart
index e24afd7..a5d4d7d 100644
--- a/packages/webview_flutter/example/test_driver/webview.dart
+++ b/packages/webview_flutter/example/test_driver/webview_flutter_e2e.dart
@@ -9,19 +9,17 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
-import 'package:flutter_driver/driver_extension.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:webview_flutter/webview_flutter.dart';
+import 'package:e2e/e2e.dart';
void main() {
- final Completer<String> allTestsCompleter = Completer<String>();
- enableFlutterDriverExtension(handler: (_) => allTestsCompleter.future);
- tearDownAll(() => allTestsCompleter.complete(null));
+ E2EWidgetsFlutterBinding.ensureInitialized();
- test('initalUrl', () async {
+ testWidgets('initalUrl', (WidgetTester tester) async {
final Completer<WebViewController> controllerCompleter =
Completer<WebViewController>();
- await pumpWidget(
+ await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: WebView(
@@ -38,10 +36,10 @@
expect(currentUrl, 'https://flutter.dev/');
});
- test('loadUrl', () async {
+ testWidgets('loadUrl', (WidgetTester tester) async {
final Completer<WebViewController> controllerCompleter =
Completer<WebViewController>();
- await pumpWidget(
+ await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: WebView(
@@ -61,11 +59,11 @@
// enable this once https://github.com/flutter/flutter/issues/31510
// is resolved.
- test('loadUrl with headers', () async {
+ testWidgets('loadUrl with headers', (WidgetTester tester) async {
final Completer<WebViewController> controllerCompleter =
Completer<WebViewController>();
final StreamController<String> pageLoads = StreamController<String>();
- await pumpWidget(
+ await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: WebView(
@@ -96,12 +94,12 @@
expect(content.contains('flutter_test_header'), isTrue);
});
- test('JavaScriptChannel', () async {
+ testWidgets('JavaScriptChannel', (WidgetTester tester) async {
final Completer<WebViewController> controllerCompleter =
Completer<WebViewController>();
final Completer<void> pageLoaded = Completer<void>();
final List<String> messagesReceived = <String>[];
- await pumpWidget(
+ await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: WebView(
@@ -137,7 +135,7 @@
expect(messagesReceived, equals(<String>['hello']));
});
- test('resize webview', () async {
+ testWidgets('resize webview', (WidgetTester tester) async {
final String resizeTest = '''
<!DOCTYPE html><html>
<head><title>Resize test</title>
@@ -184,7 +182,7 @@
javascriptMode: JavascriptMode.unrestricted,
);
- await pumpWidget(
+ await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Column(
@@ -204,7 +202,7 @@
expect(resizeCompleter.isCompleted, false);
- await pumpWidget(
+ await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Column(
@@ -222,11 +220,11 @@
await resizeCompleter.future;
});
- test('set custom userAgent', () async {
+ testWidgets('set custom userAgent', (WidgetTester tester) async {
final Completer<WebViewController> controllerCompleter1 =
Completer<WebViewController>();
final GlobalKey _globalKey = GlobalKey();
- await pumpWidget(
+ await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: WebView(
@@ -244,7 +242,7 @@
final String customUserAgent1 = await _getUserAgent(controller1);
expect(customUserAgent1, 'Custom_User_Agent1');
// rebuild the WebView with a different user agent.
- await pumpWidget(
+ await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: WebView(
@@ -260,12 +258,13 @@
expect(customUserAgent2, 'Custom_User_Agent2');
});
- test('use default platform userAgent after webView is rebuilt', () async {
+ testWidgets('use default platform userAgent after webView is rebuilt',
+ (WidgetTester tester) async {
final Completer<WebViewController> controllerCompleter =
Completer<WebViewController>();
final GlobalKey _globalKey = GlobalKey();
// Build the webView with no user agent to get the default platform user agent.
- await pumpWidget(
+ await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: WebView(
@@ -281,7 +280,7 @@
final WebViewController controller = await controllerCompleter.future;
final String defaultPlatformUserAgent = await _getUserAgent(controller);
// rebuild the WebView with a custom user agent.
- await pumpWidget(
+ await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: WebView(
@@ -295,7 +294,7 @@
final String customUserAgent = await _getUserAgent(controller);
expect(customUserAgent, 'Custom_User_Agent');
// rebuilds the WebView with no user agent.
- await pumpWidget(
+ await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: WebView(
@@ -341,12 +340,12 @@
audioTestBase64 = base64Encode(const Utf8Encoder().convert(audioTest));
});
- test('Auto media playback', () async {
+ testWidgets('Auto media playback', (WidgetTester tester) async {
Completer<WebViewController> controllerCompleter =
Completer<WebViewController>();
Completer<void> pageLoaded = Completer<void>();
- await pumpWidget(
+ await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: WebView(
@@ -373,7 +372,7 @@
pageLoaded = Completer<void>();
// We change the key to re-create a new webview as we change the initialMediaPlaybackPolicy
- await pumpWidget(
+ await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: WebView(
@@ -399,13 +398,14 @@
expect(isPaused, _webviewBool(true));
});
- test('Changes to initialMediaPlaybackPolocy are ignored', () async {
+ testWidgets('Changes to initialMediaPlaybackPolocy are ignored',
+ (WidgetTester tester) async {
final Completer<WebViewController> controllerCompleter =
Completer<WebViewController>();
Completer<void> pageLoaded = Completer<void>();
final GlobalKey key = GlobalKey();
- await pumpWidget(
+ await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: WebView(
@@ -430,7 +430,7 @@
pageLoaded = Completer<void>();
- await pumpWidget(
+ await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: WebView(
@@ -458,7 +458,7 @@
});
});
- test('getTitle', () async {
+ testWidgets('getTitle', (WidgetTester tester) async {
final String getTitleTest = '''
<!DOCTYPE html><html>
<head><title>Some title</title>
@@ -473,7 +473,7 @@
final Completer<WebViewController> controllerCompleter =
Completer<WebViewController>();
- await pumpWidget(
+ await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: WebView(
@@ -496,11 +496,6 @@
});
}
-Future<void> pumpWidget(Widget widget) {
- runApp(widget);
- return WidgetsBinding.instance.endOfFrame;
-}
-
// JavaScript booleans evaluate to different string values on Android and iOS.
// This utility method returns the string boolean value of the current platform.
String _webviewBool(bool value) {
diff --git a/packages/webview_flutter/example/test_driver/webview_test.dart b/packages/webview_flutter/example/test_driver/webview_flutter_e2e_test.dart
similarity index 72%
rename from packages/webview_flutter/example/test_driver/webview_test.dart
rename to packages/webview_flutter/example/test_driver/webview_flutter_e2e_test.dart
index b0d3305..2e5c27f 100644
--- a/packages/webview_flutter/example/test_driver/webview_test.dart
+++ b/packages/webview_flutter/example/test_driver/webview_flutter_e2e_test.dart
@@ -3,11 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
+import 'dart:io';
import 'package:flutter_driver/flutter_driver.dart';
Future<void> main() async {
final FlutterDriver driver = await FlutterDriver.connect();
- await driver.requestData(null, timeout: const Duration(minutes: 1));
+ final String result =
+ await driver.requestData(null, timeout: const Duration(minutes: 1));
driver.close();
+ exit(result == 'pass' ? 0 : 1);
}
diff --git a/packages/webview_flutter/pubspec.yaml b/packages/webview_flutter/pubspec.yaml
index 13ccef7..efddfd3 100644
--- a/packages/webview_flutter/pubspec.yaml
+++ b/packages/webview_flutter/pubspec.yaml
@@ -1,12 +1,12 @@
name: webview_flutter
description: A Flutter plugin that provides a WebView widget on Android and iOS.
-version: 0.3.14+2
+version: 0.3.15
author: Flutter Team <flutter-dev@googlegroups.com>
homepage: https://github.com/flutter/plugins/tree/master/packages/webview_flutter
environment:
sdk: ">=2.0.0-dev.68.0 <3.0.0"
- flutter: ">=1.5.0 <2.0.0"
+ flutter: ">=1.6.7 <2.0.0"
dependencies:
flutter: