Flutter:滚动时监听状态并禁用MouseHover事件
解决SingleChildScrollView滚动时MouseRegion动画异常的问题
针对你遇到的滚动时MouseRegion触发动画异常的问题,以下两个优化方案可以精准控制动画触发时机,避免滚动时的误触发:
方案1:ScrollController+滚动状态过滤动画触发
直接通过ScrollController监听滚动状态,在MouseRegion的hover事件触发前判断是否处于滚动中,仅在非滚动状态下执行动画逻辑:
class _ScrollHoverFixState extends State<ScrollHoverFix> { final ScrollController _scrollController = ScrollController(); bool _isScrolling = false; @override void initState() { super.initState(); // 监听滚动状态变化(包括惯性滚动) _scrollController.addListener(() { setState(() { _isScrolling = _scrollController.position.isScrollingNotifier.value; }); }); } @override void dispose() { _scrollController.dispose(); super.dispose(); } // 仅在非滚动时执行hover进入动画 void _triggerHoverEnterAnimation() { if (!_isScrolling) { // 这里写你的动画触发逻辑,比如: // _animationController.forward(); } } // 仅在非滚动时执行hover退出动画 void _triggerHoverExitAnimation() { if (!_isScrolling) { // 这里写你的动画恢复逻辑,比如: // _animationController.reverse(); } } @override Widget build(BuildContext context) { return SingleChildScrollView( controller: _scrollController, child: Column( children: List.generate(30, (idx) { return MouseRegion( onEnter: (_) => _triggerHoverEnterAnimation(), onExit: (_) => _triggerHoverExitAnimation(), child: Container( height: 80, margin: const EdgeInsets.symmetric(vertical: 6), color: Colors.grey[200], alignment: Alignment.center, child: Text("列表项 $idx"), ), ); }), ), ); } }
方案优势
- 不会禁用MouseRegion的其他交互(比如点击),只是过滤滚动时的动画触发
- 利用
isScrollingNotifier能准确捕捉惯性滚动状态,避免滚动结束前的误触发
方案2:NotificationListener精准监听滚动周期
通过NotificationListener捕获滚动的开始、更新、结束事件,精准标记滚动状态,比ScrollController监听更直接:
class _ScrollHoverFixState extends State<ScrollHoverFix> { bool _isScrolling = false; void _onHoverEnter() { if (!_isScrolling) { // 触发hover动画 } } void _onHoverExit() { if (!_isScrolling) { // 恢复hover动画 } } @override Widget build(BuildContext context) { return NotificationListener<ScrollNotification>( onNotification: (notification) { setState(() { // 滚动开始/进行中时标记为true,结束时标记为false _isScrolling = notification is ScrollStartNotification || notification is ScrollUpdateNotification; }); return false; // 允许通知继续传递给子组件 }, child: SingleChildScrollView( child: Column( children: List.generate(30, (idx) { return MouseRegion( onEnter: (_) => _onHoverEnter(), onExit: (_) => _onHoverExit(), child: Container( height: 80, margin: const EdgeInsets.symmetric(vertical: 6), color: Colors.grey[200], alignment: Alignment.center, child: Text("列表项 $idx"), ), ); }), ), ), ); } }
方案优势
- 无需额外维护ScrollController,直接通过滚动通知获取状态
- 能实时响应滚动的开始和结束,状态更新无延迟
之前方案未达预期的可能原因
- IgnorePointer方案:直接禁用了MouseRegion的所有交互,包括点击等非动画需求的操作,而且状态更新不及时的话,会出现滚动结束后仍无法触发动画的情况
- Scrollable.of(context)方案:如果Widget树中Scrollable的上下文获取错误(比如嵌套ScrollView),会导致获取的position不准确,或者没有结合setState及时更新滚动状态,导致动画判断逻辑失效
内容的提问来源于stack exchange,提问作者Hippo Fish
相关产品推荐
相关产品推荐

