Skip to content

Commit

Permalink
Added a test for the pull-to-refresh feature when used on Android.
Browse files Browse the repository at this point in the history
  • Loading branch information
pichillilorenzo committed Mar 6, 2021
1 parent 0744743 commit 237ba6e
Show file tree
Hide file tree
Showing 13 changed files with 85 additions and 23 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 5.1.0+1

- Added a test for the pull-to-refresh feature when used on Android. It requires the `useHybridComposition: true` Android-specific option, otherwise it will throw an exception.

## 5.1.0

- Added support for pull-to-refresh feature [#395](https://github.com/pichillilorenzo/flutter_inappwebview/issues/395)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import android.webkit.WebView;
import android.webkit.WebViewClient;

import androidx.annotation.NonNull;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewFeature;

Expand Down Expand Up @@ -74,15 +75,17 @@ public FlutterWebView(BinaryMessenger messenger, final Context context, Object i
}
}

webView = new InAppWebView(context, channel, id, windowId, options, contextMenu, containerView, userScripts);
webView = new InAppWebView(context, channel, id, windowId, options, contextMenu, options.useHybridComposition ? null : containerView, userScripts);
displayListenerProxy.onPostWebViewInitialization(displayManager);

MethodChannel pullToRefreshLayoutChannel = new MethodChannel(messenger, "com.pichillilorenzo/flutter_inappwebview_pull_to_refresh_" + id);
PullToRefreshOptions pullToRefreshOptions = new PullToRefreshOptions();
pullToRefreshOptions.parse(pullToRefreshInitialOptions);
pullToRefreshLayout = new PullToRefreshLayout(context, pullToRefreshLayoutChannel, pullToRefreshOptions);
pullToRefreshLayout.addView(webView);
pullToRefreshLayout.prepare();
if (options.useHybridComposition) {
MethodChannel pullToRefreshLayoutChannel = new MethodChannel(messenger, "com.pichillilorenzo/flutter_inappwebview_pull_to_refresh_" + id);
PullToRefreshOptions pullToRefreshOptions = new PullToRefreshOptions();
pullToRefreshOptions.parse(pullToRefreshInitialOptions);
pullToRefreshLayout = new PullToRefreshLayout(context, pullToRefreshLayoutChannel, pullToRefreshOptions);
pullToRefreshLayout.addView(webView);
pullToRefreshLayout.prepare();
}

methodCallDelegate = new InAppWebViewMethodHandler(webView);
channel.setMethodCallHandler(methodCallDelegate);
Expand Down Expand Up @@ -127,7 +130,7 @@ else if (initialUrlRequest != null) {

@Override
public View getView() {
return pullToRefreshLayout;
return pullToRefreshLayout != null ? pullToRefreshLayout : webView;
}

@Override
Expand Down Expand Up @@ -170,26 +173,26 @@ public void onPageFinished(WebView view, String url) {

@Override
public void onInputConnectionLocked() {
if (webView != null && webView.inAppBrowserDelegate == null)
if (webView != null && webView.inAppBrowserDelegate == null && !webView.options.useHybridComposition)
webView.lockInputConnection();
}

@Override
public void onInputConnectionUnlocked() {
if (webView != null && webView.inAppBrowserDelegate == null)
if (webView != null && webView.inAppBrowserDelegate == null && !webView.options.useHybridComposition)
webView.unlockInputConnection();
}

@Override
public void onFlutterViewAttached(View flutterView) {
if (webView != null) {
public void onFlutterViewAttached(@NonNull View flutterView) {
if (webView != null && !webView.options.useHybridComposition) {
webView.setContainerView(flutterView);
}
}

@Override
public void onFlutterViewDetached() {
if (webView != null) {
if (webView != null && !webView.options.useHybridComposition) {
webView.setContainerView(null);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ public InAppWebView(Context context, MethodChannel channel, Object id,
@Nullable Integer windowId, InAppWebViewOptions options,
@Nullable Map<String, Object> contextMenu, View containerView,
List<UserScript> userScripts) {
super(context, containerView);
super(context, containerView, options.useHybridComposition);
this.channel = channel;
this.id = id;
this.windowId = windowId;
Expand Down Expand Up @@ -1244,7 +1244,7 @@ public boolean dispatchTouchEvent(MotionEvent event) {
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
InputConnection connection = super.onCreateInputConnection(outAttrs);
if (connection == null && containerView != null) {
if (connection == null && !options.useHybridComposition && containerView != null) {
// workaround to hide the Keyboard when the user click outside
// on something not focusable such as input or a textarea.
containerView
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import android.webkit.WebView;
import android.widget.ListPopupWindow;

import androidx.annotation.Nullable;

/**
* A WebView subclass that mirrors the same implementation hacks that the system WebView does in
* order to correctly create an InputConnection.
Expand All @@ -22,13 +24,16 @@
*/
public class InputAwareWebView extends WebView {
private static final String LOG_TAG = "InputAwareWebView";
@Nullable
public View containerView;
private View threadedInputConnectionProxyView;
private ThreadedInputConnectionProxyAdapterView proxyAdapterView;
private boolean useHybridComposition = false;

public InputAwareWebView(Context context, View containerView) {
public InputAwareWebView(Context context, @Nullable View containerView, Boolean useHybridComposition) {
super(context);
this.containerView = containerView;
this.useHybridComposition = useHybridComposition == null ? false : useHybridComposition;
}

public InputAwareWebView(Context context, AttributeSet attrs) {
Expand Down Expand Up @@ -83,6 +88,9 @@ public void unlockInputConnection() {

/** Restore the original InputConnection, if needed. */
void dispose() {
if (useHybridComposition) {
return;
}
resetInputConnection();
}

Expand All @@ -101,6 +109,9 @@ void dispose() {
*/
@Override
public boolean checkInputConnectionProxy(final View view) {
if (useHybridComposition) {
return super.checkInputConnectionProxy(view);
}
// Check to see if the view param is WebView's ThreadedInputConnectionProxyView.
View previousProxy = threadedInputConnectionProxyView;
threadedInputConnectionProxyView = view;
Expand Down Expand Up @@ -138,6 +149,10 @@ public boolean checkInputConnectionProxy(final View view) {
@Override
public void clearFocus() {
super.clearFocus();

if (useHybridComposition) {
return;
}
resetInputConnection();
}

Expand Down Expand Up @@ -206,6 +221,10 @@ public void run() {

@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
if (useHybridComposition) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
return;
}
// This works around a crash when old (<67.0.3367.0) Chromium versions are used.

// Prior to Chromium 67.0.3367 the following sequence happens when a select drop down is shown
Expand Down
2 changes: 1 addition & 1 deletion example/.flutter-plugins-dependencies
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.0-nullsafety/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.1.0+2/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.6/","dependencies":[]}],"android":[{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.0-nullsafety/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.1.0+2/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.6/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.5-nullsafety/","dependencies":[]},{"name":"url_launcher_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.2.0-nullsafety/","dependencies":[]},{"name":"url_launcher_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.1.0-nullsafety.3/","dependencies":[]},{"name":"url_launcher_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"integration_test","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_linux","url_launcher_macos","url_launcher_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2021-03-05 22:47:49.023558","version":"2.1.0-10.0.pre"}
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.0-nullsafety/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.1.0+2/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.6/","dependencies":[]}],"android":[{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.0-nullsafety/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.1.0+2/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.6/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.5-nullsafety/","dependencies":[]},{"name":"url_launcher_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.2.0-nullsafety/","dependencies":[]},{"name":"url_launcher_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.1.0-nullsafety.3/","dependencies":[]},{"name":"url_launcher_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"integration_test","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_linux","url_launcher_macos","url_launcher_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2021-03-06 16:03:42.653087","version":"2.1.0-10.0.pre"}
5 changes: 5 additions & 0 deletions example/integration_test/webview_flutter_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4412,6 +4412,11 @@ setTimeout(function() {
child: InAppWebView(
key: GlobalKey(),
initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')),
initialOptions: InAppWebViewGroupOptions(
android: AndroidInAppWebViewOptions(
useHybridComposition: true
)
),
pullToRefreshController: pullToRefreshController,
onWebViewCreated: (controller) {
controllerCompleter.complete(controller);
Expand Down
18 changes: 18 additions & 0 deletions example/ios/Flutter/Flutter.podspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#
# NOTE: This podspec is NOT to be published. It is only used as a local source!
# This is a generated file; do not edit or check into version control.
#

Pod::Spec.new do |s|
s.name = 'Flutter'
s.version = '1.0.0'
s.summary = 'High-performance, high-fidelity mobile apps.'
s.homepage = 'https://flutter.io'
s.license = { :type => 'MIT' }
s.author = { 'Flutter Dev Team' => '[email protected]' }
s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s }
s.ios.deployment_target = '8.0'
# Framework linking is handled by Flutter tooling, not CocoaPods.
# Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs.
s.vendored_frameworks = 'path/to/nothing'
end
7 changes: 4 additions & 3 deletions example/ios/Flutter/flutter_export_environment.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
# This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=/Users/lorenzopichilli/flutter"
export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example"
export "FLUTTER_TARGET=lib/main.dart"
export "FLUTTER_TARGET=integration_test/webview_flutter_test.dart"
export "FLUTTER_BUILD_DIR=build"
export "SYMROOT=${SOURCE_ROOT}/../build/ios"
export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1"
export "DART_DEFINES=RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ=="
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=false"
export "TRACK_WIDGET_CREATION=true"
export "TREE_SHAKE_ICONS=false"
export "PACKAGE_CONFIG=.packages"
export "PACKAGE_CONFIG=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example/.dart_tool/package_config.json"
1 change: 0 additions & 1 deletion lib/src/in_app_browser/in_app_browser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,6 @@ class InAppBrowser {
Future<void> show() async {
this.throwIfNotOpened();
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('id', () => id);
await _channel.invokeMethod('show', args);
}

Expand Down
8 changes: 7 additions & 1 deletion lib/src/in_app_webview/in_app_webview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,13 @@ class _InAppWebViewState extends State<InAppWebView> {
@override
Widget build(BuildContext context) {
if (defaultTargetPlatform == TargetPlatform.android) {
if (widget.initialOptions?.android.useHybridComposition ?? false) {
var useHybridComposition = widget.initialOptions?.android.useHybridComposition ?? false;

if (!useHybridComposition && widget.pullToRefreshController != null) {
throw new Exception("To use the pull-to-refresh feature, useHybridComposition Android-specific option MUST be true!");
}

if (useHybridComposition) {
return PlatformViewLink(
viewType: 'com.pichillilorenzo/flutter_inappwebview',
surfaceFactory: (
Expand Down
4 changes: 4 additions & 0 deletions lib/src/in_app_webview/webview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import 'in_app_webview_controller.dart';
import 'in_app_webview_options.dart';
import 'headless_in_app_webview.dart';

import 'android/in_app_webview_options.dart';

///Abstract class that represents a WebView. Used by [InAppWebView] and [HeadlessInAppWebView].
abstract class WebView {
///The window id of a [CreateWindowAction.windowId].
Expand Down Expand Up @@ -645,6 +647,8 @@ abstract class WebView {
final UnmodifiableListView<UserScript>? initialUserScripts;

///Represents the pull-to-refresh feature controller.
///
///**NOTE for Android**: to be able to use the "pull-to-refresh" feature, [AndroidInAppWebViewOptions.useHybridComposition] must be `true`.
final PullToRefreshController? pullToRefreshController;

WebView(
Expand Down
3 changes: 3 additions & 0 deletions lib/src/pull_to_refresh/pull_to_refresh_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import '../in_app_webview/webview.dart';
import '../in_app_browser/in_app_browser.dart';
import '../util.dart';
import '../types.dart';
import '../in_app_webview/android/in_app_webview_options.dart';
import 'pull_to_refresh_options.dart';

///A standard controller that can initiate the refreshing of a scroll view’s contents.
///This should be used whenever the user can refresh the contents of a WebView via a vertical swipe gesture.
///
///All the methods should be called only when the WebView has been created or is already running
///(for example [WebView.onWebViewCreated] or [InAppBrowser.onBrowserCreated]).
///
///**NOTE for Android**: to be able to use the "pull-to-refresh" feature, [AndroidInAppWebViewOptions.useHybridComposition] must be `true`.
class PullToRefreshController {
late PullToRefreshOptions options;
MethodChannel? _channel;
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: flutter_inappwebview
description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
version: 5.1.0
version: 5.1.0+1
homepage: https://github.com/pichillilorenzo/flutter_inappwebview

environment:
Expand Down

0 comments on commit 237ba6e

Please sign in to comment.