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

如何在模态底部弹窗中实现可滚动且支持滑动关闭的WebViewWidget?

问题

我把WebViewWidget放在模态底部弹窗(modal bottom sheet)里,想要实现两个效果:

  1. WebView可以滚动;
  2. 底部弹窗能通过滑动手势关闭。

具体来说,当WebView处于顶部位置时,向下滚动的手势要传递给底部弹窗,类似原生Android的嵌套滚动机制。

默认情况下WebView没法滚动,我知道WebViewWidget支持通过gestureRecognizers指定手势处理器,用EagerGestureRecognizer能实现WebView滚动,但会导致底部弹窗关不了。有没有常规的解决方案?

两种配置的表现:

  • 手势识别器设为gestureRecognizers: const {}时:
    无手势效果
  • 使用gestureRecognizers: { Factory(() => EagerGestureRecognizer()) }时:
    使用EagerGestureRecognizer的效果

最小可复现代码:

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

Future<void> main() async {
  runApp(const MaterialApp(home: DemoApp()));
}

class DemoApp extends StatelessWidget {
  const DemoApp({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: FilledButton(
          child: const Text('Show WebView'),
          onPressed: () => showWebView(context),
        ),
      ),
    );
  }

  void showWebView(BuildContext context) {
    final controller = WebViewController
        .fromPlatformCreationParams(const PlatformWebViewControllerCreationParams())
        ..loadRequest(Uri.parse('https://stackoverflow.com/questions/62276050/why-cant-i-scroll-custom-webview-in-flutter-modalbottomsheet'));

    showModalBottomSheet(
      context: context,
      builder: (BuildContext context) => Scaffold(
        body: WebViewWidget(
          controller: controller,
          gestureRecognizers: { Factory(() => EagerGestureRecognizer()) },
        ),
      ),
    );
  }
}
解决方案

要实现WebView滚动和底部弹窗滑动关闭的嵌套效果,核心是让手势能根据WebView的滚动状态动态决定由谁处理,可通过以下步骤实现:

  1. 自定义手势识别器:创建继承自VerticalDragGestureRecognizer的类,在WebView滚动到顶部时放弃手势优先级,让底部弹窗的滑动手势生效;否则拦截手势给WebView处理。
  2. 监听WebView滚动位置:通过WebViewController的滚动监听,实时获取当前WebView的滚动偏移,判断是否处于顶部。
  3. 配置WebViewWidget手势识别器:将自定义的手势识别器传入gestureRecognizers参数。

修改后的完整代码:

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

Future<void> main() async {
  runApp(const MaterialApp(home: DemoApp()));
}

class DemoApp extends StatelessWidget {
  const DemoApp({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: FilledButton(
          child: const Text('显示WebView'),
          onPressed: () => showWebView(context),
        ),
      ),
    );
  }

  void showWebView(BuildContext context) {
    final controller = WebViewController
        .fromPlatformCreationParams(const PlatformWebViewControllerCreationParams())
        ..loadRequest(Uri.parse('https://stackoverflow.com/questions/62276050/why-cant-i-scroll-custom-webview-in-flutter-modalbottomsheet'));

    // 存储WebView滚动位置
    ValueNotifier<double> scrollOffset = ValueNotifier(0);

    // 监听WebView滚动事件
    controller.addScrollListener((x, y) {
      scrollOffset.value = y;
    });

    showModalBottomSheet(
      context: context,
      builder: (BuildContext context) => Scaffold(
        body: WebViewWidget(
          controller: controller,
          gestureRecognizers: {
            Factory(() => _NestedScrollGestureRecognizer(scrollOffset: scrollOffset))
          },
        ),
      ),
    );
  }
}

// 自定义嵌套滚动手势识别器
class _NestedScrollGestureRecognizer extends VerticalDragGestureRecognizer {
  final ValueNotifier<double> scrollOffset;

  _NestedScrollGestureRecognizer({required this.scrollOffset});

  @override
  void acceptGesture(int pointer) {
    // WebView不在顶部时,由WebView处理滚动手势
    if (scrollOffset.value > 1) {
      super.acceptGesture(pointer);
    } else {
      // WebView在顶部时,放弃手势,让底部弹窗处理滑动关闭
      rejectGesture(pointer);
    }
  }
}

说明

  • 自定义的_NestedScrollGestureRecognizer会根据WebView的滚动偏移判断手势归属:滚动偏移大于1时(避免精度误差),WebView处理滚动;处于顶部时,让底部弹窗的滑动关闭手势生效。
  • 通过controller.addScrollListener实时同步WebView的滚动位置,保证手势判断的准确性。
  • 该方案模拟原生嵌套滚动逻辑,同时满足WebView滚动和底部弹窗关闭的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 17:02:53