Flutter PageView.builder的onPageChanged如何仅在完整页面滑动完成后触发
解决方案
问题根因
onPageChanged的触发逻辑为页面占据视口比例超过50%时即回调,此时滑动动作尚未完成,你在回调中调用setState修改physics会直接中断当前滑动,同时触发页面重建,此时索引已更新但旧页面还未退出视口,就会出现动画绑定到旧页面的异常。
实现方案
推荐使用NotificationListener监听PageScrollEndNotification,该通知仅在页面完全滑动结束、吸附完成后触发,完全符合你的需求。
步骤1:声明并初始化PageController
// 类属性声明 late PageController _pageController; int _currentIndex = 0; @override void initState() { super.initState(); _pageController = PageController(); } @override void dispose() { _pageController.dispose(); super.dispose(); }
步骤2:用NotificationListener包裹PageView,移除原onPageChanged里的状态修改逻辑
@override Widget build(BuildContext context) { return Material( child: NotificationListener<PageScrollEndNotification>( onNotification: (notification) { // 滚动结束后获取当前索引 _currentIndex = _pageController.page!.round(); print('页面滑动完成,当前索引:$_currentIndex'); setState(() { _canScroll = false; // 这里可以处理新页面动画触发逻辑,比如更新_cardAnimType参数 }); // 返回true表示消费该通知,不再向上冒泡 return true; }, child: PageView.builder( controller: _pageController, // 原onPageChanged可以保留做无状态修改的逻辑,不需要的话直接删除 onPageChanged: (int i) { print('页面切换中,当前索引:$i'); }, physics: _canScroll ? const AlwaysScrollableScrollPhysics() : const NeverScrollableScrollPhysics(), pageSnapping: true, itemBuilder: (context, index) { return WordCardWidget( word1: _word1, word2: _word2, answer: answer, // 仅当前完全显示的页面才传动画触发标识 animationType: index == _currentIndex ? _cardAnimType : null, buttonSelected: buttonSelected, ); }, ), ) ); }
优化建议
- 如果你的
WordCardWidget的动画是页面显示时自动触发,可以在WordCardWidget内部通过Visibility或者自己维护的激活状态判断,只有当卡片完全可见时再启动动画,避免预加载的页面提前播放动画。 - 如果你不需要监听滚动中的索引变化,可以直接删除
onPageChanged参数,减少不必要的回调。
内容的提问来源于stack exchange,提问作者Kdon
相关产品推荐
相关产品推荐

