pichillilorenzo / flutter_inappwebview

A Flutter plugin that allows you to add an inline webview, to use a headless webview, and to open an in-app browser window.
https://inappwebview.dev
Apache License 2.0
3.28k stars 1.62k forks source link

Gestures don't work well when using gestures over inappwebview. #702

Closed delay closed 3 years ago

delay commented 3 years ago

So there is a problem when using multiple gesture recognizers on top of a webview in iOS (have not tested with android). This stackoverflow issue explains the problem and has a solution for the webview_flutter package. https://stackoverflow.com/questions/57069716/scrolling-priority-when-combining-horizontal-scrolling-with-webview/57150906#57150906 . Basically when your gesture matches a custom gesture it always wins. Ultimately this prevents the webview from being able to scroll unless it receives a gesture that does not overlap with a custom gesture I setup in a stack.

I have created a mimimum reproduction so you can see this issue. This project will allow you to swipe left and right between three pageviews. When you try to scroll vertically it only works if you do a perfectly vertical swipe so as not to also activate the pageview gesture detector.

If you comment out your package import and uncomment out the webview_flutter one.
Then replace this line runApp(InappWebViewUI()); with this one runApp(WebViewExample()); you can see how scrolling works ok with the stackoverflow fix and the gesture is passed to the webview even when it is not a perfect vertical scroll.

The only solution I know of is if you can add gesture recognizers similar to webview_flutter package. gestureRecognizers: [ Factory(() => PlatformViewVerticalGestureRecognizer()), ].toSet(),

I don't know if this is doable or not on your end but it would allow me to activate flutter widgets when performing gestures over inappwebview. I am using gestures in my own app so I can swipe left and right on the webview to open and close a custom drawer. Maybe you know of a different way to fix this issue?

import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter/foundation.dart';
//import 'package:webview_flutter/webview_flutter.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  debugPrintGestureArenaDiagnostics = true;
  runApp(InappWebViewUI()); //WebViewExample()
}

class InappWebViewUI extends StatelessWidget {
  final GlobalKey webViewKey = GlobalKey();

  InAppWebViewController? webView;
  late ContextMenu contextMenu;
  String url = "";
  double progress = 0;
  var options = InAppWebViewGroupOptions(
      crossPlatform: InAppWebViewOptions(
        useShouldOverrideUrlLoading: false,
        mediaPlaybackRequiresUserGesture: false,
      ),
      android: AndroidInAppWebViewOptions(
        useHybridComposition: true,
      ),
      ios: IOSInAppWebViewOptions(
        allowsInlineMediaPlayback: true,
      ));

  @override
  Widget build(BuildContext context) {
    //print('updated');
    return PageView.builder(
      itemCount: 3,
      itemBuilder: (context, index) {
        return InAppWebView(
          key: webViewKey,
          // contextMenu: contextMenu,
          initialUrlRequest:
              URLRequest(url: Uri.parse("https://flutter.dev/docs")),
          // initialFile: "assets/index.html",
          //initialUserScripts: UnmodifiableListView<UserScript>([]),
          initialOptions: options,
          onWebViewCreated: (controller) {
            webView = controller;
            print("onWebViewCreated");
          },
          onLoadStart: (controller, url) {
            print("onLoadStart $url");
          },
          androidOnPermissionRequest: (InAppWebViewController controller,
              String origin, List<String> resources) async {
            return PermissionRequestResponse(
                resources: resources,
                action: PermissionRequestResponseAction.GRANT);
          },
          shouldOverrideUrlLoading: (controller, navigationAction) async {
            var uri = navigationAction.request.url!;

            if (![
              "http",
              "https",
              "file",
              "chrome",
              "data",
              "javascript",
              "about"
            ].contains(uri.scheme)) {}

            return NavigationActionPolicy.ALLOW;
          },
          onLoadStop: (controller, url) async {
            print("onLoadStop $url");

            webView = controller;
          },
          onProgressChanged: (controller, progress) {},
          onUpdateVisitedHistory: (controller, url, androidIsReload) {
            print("onUpdateVisitedHistory $url");
          },
          onConsoleMessage: (controller, consoleMessage) {
            print(consoleMessage);
          },
        );
      },
    );
  }
}

class WebViewExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //print('updated');
    return PageView.builder(
      itemCount: 3,
      itemBuilder: (context, index) {
        return WebView(
          initialUrl: 'https://flutter.dev/docs',
          gestureRecognizers: [
            Factory(() => PlatformViewVerticalGestureRecognizer()),
          ].toSet(),
        );
      },
    );
  }
}

class PlatformViewVerticalGestureRecognizer
    extends VerticalDragGestureRecognizer {
  PlatformViewVerticalGestureRecognizer({PointerDeviceKind? kind})
      : super(kind: kind);

  Offset _dragDistance = Offset.zero;

  @override
  void addPointer(PointerEvent event) {
    startTrackingPointer(event.pointer);
  }

  @override
  void handleEvent(PointerEvent event) {
    _dragDistance = _dragDistance + event.delta;
    if (event is PointerMoveEvent) {
      final double dy = _dragDistance.dy.abs();
      final double dx = _dragDistance.dx.abs();

      if (dy > dx && dy > kTouchSlop) {
        // vertical drag - accept
        resolve(GestureDisposition.accepted);
        _dragDistance = Offset.zero;
      } else if (dx > kTouchSlop && dx > dy) {
        // horizontal drag - stop tracking
        stopTrackingPointer(event.pointer);
        _dragDistance = Offset.zero;
      }
    }
  }

  @override
  String get debugDescription => 'horizontal drag (platform view)';

  @override
  void didStopTrackingLastPointer(int pointer) {}
}

Environment

Technology Version
Flutter version 2.0.0
Plugin version ^5.0.4-nullsafety.1
Android version
iOS version
Xcode version
pichillilorenzo commented 3 years ago

You can set the same gestureRecognizers also in InAppWebView. The problem here is that you are setting the same GlobalKey webViewKey for all 3 InAppWebViews. You need to be careful with widget keys!

Simple working example:

class InappWebViewUI extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Directionality(
        textDirection: TextDirection.ltr,
        child: PageView.builder(
          itemCount: 3,
          itemBuilder: (context, index) {
            return InAppWebView(
              initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev/docs')),
              gestureRecognizers: [
                Factory(() => PlatformViewVerticalGestureRecognizer()),
              ].toSet(),
            );
          },
        )
    );
  }
}
delay commented 3 years ago

Ok thanks for the info! Somehow missed that you could set them in inappwebview. Also thanks for the key info. I will double check that I am using unique keys in my own project.

github-actions[bot] commented 4 days ago

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug and a minimal reproduction of the issue.