Flutter WebView下拉刷新异常:触发后点击屏幕自动刷新
问题原因分析
你的问题核心出在自定义手势识别器的逻辑错误:
PlatformViewVerticalGestureRecognizer的handleEvent会响应所有指针事件(包括点击、滑动),只要WebView的滚动位置scrollY <5,就会触发刷新。首次下拉后页面回到顶部,后续点击时scrollY仍然小于5,所以每次点击都会触发reload()。- 同时外层ListView的
_scrollListener也在监听滚动到顶部的事件,双重触发刷新逻辑,进一步加剧问题。
修复方案
1. 重构手势识别器,只响应下拉滑动动作
修改PlatformViewVerticalGestureRecognizer,仅当垂直下拉距离超过触摸阈值时才触发刷新,并且区分点击和滑动事件:
class PlatformViewVerticalGestureRecognizer extends VerticalDragGestureRecognizer { PlatformViewVerticalGestureRecognizer({PointerDeviceKind? kind}) : super(kind: kind); Offset _dragDistance = Offset.zero; // 标记是否已经触发过刷新,避免一次滑动多次触发 bool _hasTriggeredRefresh = false; @override void addPointer(PointerEvent event) { startTrackingPointer(event.pointer); // 每次开始触摸时重置状态 _dragDistance = Offset.zero; _hasTriggeredRefresh = false; } @override void handleEvent(PointerEvent event) { if (event is PointerMoveEvent) { _dragDistance += event.delta; // 仅处理下拉动作(dy为正,代表向下滑动),且超过触摸阈值 if (_dragDistance.dy > kTouchSlop && !_hasTriggeredRefresh) { controllerGlobal.getScrollY().then((value) { if (value < 5) { Fluttertoast.showToast( msg: "Refreshing...", toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.CENTER, timeInSecForIosWeb: 1, backgroundColor: Color.fromARGB(255, 179, 158, 157), textColor: Colors.white, fontSize: 16.0, ); controllerGlobal.reload(); _hasTriggeredRefresh = true; } }); } else if (_dragDistance.dy < -kTouchSlop) { // 上滑时,不处理,交给WebView自身滚动 resolve(GestureDisposition.rejected); } } else if (event is PointerUpEvent || event is PointerCancelEvent) { // 触摸结束时重置状态 _dragDistance = Offset.zero; _hasTriggeredRefresh = false; stopTrackingPointer(event.pointer); } } @override String get debugDescription => 'vertical drag for refresh (platform view)'; @override void didStopTrackingLastPointer(int pointer) {} }
2. 移除冗余的ScrollController监听
外层ListView包裹WebView的结构本身容易引发手势冲突,且_scrollListener的逻辑会重复触发刷新,直接移除相关代码:
class WebViewExampleState extends State<WebViewEx> { late bool check, check1; // 移除ScrollController相关代码 @override void initState() { super.initState(); check = false; check1 = false; } // 移除_scrollListener方法 @override Widget build(BuildContext context) { return Scaffold( body: WillPopScope( onWillPop: () async { if (await controllerGlobal.canGoBack()) { await controllerGlobal.goBack(); return Future.value(false); } return Future.value(true); }, // 移除外层ListView,直接使用WebView作为Scaffold的body child: Container( height: MediaQuery.of(context).size.height, child: WebView( initialUrl: 'https://doorstep.pro/user/login', gestureNavigationEnabled: true, debuggingEnabled: true, gestureRecognizers: [ Factory(() => PlatformViewVerticalGestureRecognizer(kind: null)), ].toSet(), javascriptMode: JavascriptMode.unrestricted, onPageFinished: (String url) { setState(() { bool temp = check; check = true; if (!temp) controllerGlobal.scrollBy(0, 10); }); }, onWebViewCreated: (WebViewController webViewController) { controllerGlobal = webViewController; }, ), ), ), ); } @override void dispose() { super.dispose(); } goBack() {} }
3. 优化返回按钮逻辑
原onWillPop逻辑不管能不能返回都执行goBack(),会引发错误,修复为只有当WebView可以返回时才执行返回操作。
修复后效果
- 只有当在WebView顶部向下滑动超过触摸阈值时,才会触发刷新。
- 点击屏幕任意区域不会触发刷新。
- 避免了重复刷新的逻辑冲突。
内容的提问来源于stack exchange,提问作者Comfort Chambeshi
相关产品推荐
相关产品推荐

