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
相关产品推荐
相关产品推荐

