You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中InAppWebView与GestureDetector滑动事件冲突解决方案咨询

实现方案

要解决InAppWebView与外部GestureDetector的水平手势冲突,核心思路是让WebView仅在需要处理水平滚动时拦截手势,否则将事件传递给外部手势检测器。具体实现步骤如下:


1. 关键逻辑设计

  • 保存InAppWebViewController,用于查询网页的滚动状态(当前滚动位置、最大滚动范围)
  • 监听网页滚动事件,实时更新滚动状态
  • 在外部手势回调中,判断网页是否具备水平滚动能力,或是否已滚动到水平边界:
    • 若网页无法水平滚动,直接处理外部手势
    • 若网页已滚动到左/右边界,此时WebView无需再处理水平滑动,将事件交给外部GestureDetector

2. 修改后的完整代码

class TestWidget extends StatefulWidget {
  const TestWidget({super.key});

  @override
  State<TestWidget> createState() => _TestWidgetState();
}

class _TestWidgetState extends State<TestWidget> {
  late Offset downPos;
  double slidingDistance = 0;
  
  // 新增变量:WebView控制器、滚动状态
  InAppWebViewController? _webViewController;
  double _scrollMaxX = 0; // 水平最大滚动距离
  double _currentScrollX = 0; // 当前水平滚动位置

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      behavior: HitTestBehavior.translucent,
      child: Stack(
        children: [
          InAppWebView(
            initialUrlRequest: URLRequest(
              url: Uri.parse('http://www.baidu.com'),
            ),
            initialOptions: InAppWebViewGroupOptions(
              crossPlatform: InAppWebViewOptions(
                useShouldOverrideUrlLoading: true,
              ),
            ),
            onLoadStart: (controller, url) {},
            onLoadStop: (controller, url) async {
              _webViewController = controller;
              // 加载完成后获取水平最大滚动距离
              _scrollMaxX = await controller.getScrollMaxX() ?? 0;
            },
            onScrollChanged: (controller, x, y) {
              // 实时更新当前滚动位置和最大滚动距离
              setState(() {
                _currentScrollX = x;
                _scrollMaxX = controller.getScrollMaxX() ?? 0;
              });
            },
            shouldOverrideUrlLoading: (controller, navigationAction) async {
              if (await _openThirdApp(navigationAction)) {
                return NavigationActionPolicy.CANCEL;
              }
              return NavigationActionPolicy.ALLOW;
            },
            onProgressChanged: (controller, progress) {},
            onLoadError: (controller, url, code, message) {},
          ),
          Align(
            alignment: Alignment.centerLeft,
            child: Transform.translate(
              offset: Offset(
                slidingDistance - 200.w,
                0,
              ),
              child: Container(
                width: 200.w,
                height: 50.h,
                color: Colors.red,
              ),
            ),
          ),
        ],
      ),
      onHorizontalDragStart: (d) {
        downPos = d.localPosition;
      },
      onHorizontalDragUpdate: (d) {
        // 判断网页是否能水平滚动,或是否已到水平边界
        bool canWebScrollHorizontally = _scrollMaxX > 0;
        bool isAtLeftEdge = _currentScrollX <= 0 && d.delta.dx > 0; // 左边界且向右滑
        bool isAtRightEdge = _currentScrollX >= _scrollMaxX && d.delta.dx < 0; // 右边界且向左滑

        // 仅当网页无法滚动,或已到边界时,处理外部手势
        if (!canWebScrollHorizontally || isAtLeftEdge || isAtRightEdge) {
          final move = d.localPosition;
          setState(() {
            slidingDistance = (downPos.dx - move.dx).abs();
          });
        }
      },
      onHorizontalDragEnd: (d) {
        setState(() {
          slidingDistance = 0;
        });
      },
    );
  }

  Future<bool> _openThirdApp(NavigationAction navigationAction) async {
    final uri = navigationAction.request.url!;
    if (!['http', 'https', 'file', 'chrome', 'data', 'javascript', 'about']
        .contains(uri.scheme)) {
      if (await canLaunchUrl(uri)) {
        await launchUrl(uri);
      }
      return true;
    }
    return false;
  }
}

3. 核心说明

  • 滚动状态监听:通过onScrollChanged实时获取网页的水平滚动位置和最大滚动范围,判断网页是否具备水平滚动能力
  • 手势拦截判断:在onHorizontalDragUpdate中,只有当网页无法水平滚动,或者已经滚动到左右边界时,才触发外部手势的滑动逻辑
  • 控制器保存:在onLoadStop中保存WebView控制器,用于查询滚动相关参数

内容的提问来源于stack exchange,提问作者Para

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 15:39:57