实现类Instagram故事的带弹跳效果线性进度动画遇阻
问题描述
我想实现类似Instagram故事的进度条动画效果,目前使用flutter_story库,在点击跳过或切换到下一个故事时,进度动画的表现不符合预期。以下是我的实现代码,恳请帮忙修复:
下一个故事按钮点击事件
AnimationController? _progressController; late Animation<double> _animation; _progressController = AnimationController( duration: Duration.zero, vsync: this, ); _setAnimation(); void _nextStory() { List<StoryCard> cards = widget.children[_storyIndex].children; if (_cardIndex >= cards.length) return; _progressController?.reset(); cards[_cardIndex].onDispose?.call(_cardIndex); if (_cardIndex < cards.length - 1) { _setStoryCardVisited(_storyIndex, _cardIndex, true); _playStory(isCallVisit: true); _isTapped = false; cards[_cardIndex].onNext?.call(_cardIndex); _setAnimation(); // _progressController?.forward(); } else { _pageController ?.nextPage( duration: const Duration(milliseconds: 250), curve: Curves.easeOut) .then((_) => cards[_cardIndex].onNext?.call(_cardIndex)); } void _setAnimation() { _animation = Tween<double>(begin: 0.0, end: 1.0).animate( CurvedAnimation( parent: _progressController!, curve: Curves.easeOutBack, // Bounce forward effect ), )..addStatusListener((status) { if (status == AnimationStatus.completed) { _nextStory(); } }); }
动画容器
AnimatedBuilder( animation: widget.animation, builder: (context, child) { return Stack( children: [ LinearProgressIndicator( value: widget.animation.value, backgroundColor: Colors.grey, valueColor: AlwaysStoppedAnimation<Color>(Colors.white), ), ], ); }, )
解决方案
你的代码存在几个关键问题导致动画异常,以下是修复后的完整逻辑:
1. 修正动画控制器初始化
_progressController的时长不能设为Duration.zero,需要设置为单条故事的展示时长(比如5秒):
_progressController = AnimationController( duration: const Duration(seconds: 5), // 单条故事的默认展示时长 vsync: this, );
2. 完善_nextStory逻辑
切换故事时,需要先终止当前动画、更新卡片索引,再启动下一个故事的动画:
void _nextStory() { List<StoryCard> cards = widget.children[_storyIndex].children; if (_cardIndex >= cards.length) return; // 先停止并重置当前动画,避免状态冲突 _progressController?.stop(); _progressController?.reset(); cards[_cardIndex].onDispose?.call(_cardIndex); if (_cardIndex < cards.length - 1) { _setStoryCardVisited(_storyIndex, _cardIndex, true); _cardIndex++; // 切换到下一个卡片 _isTapped = false; cards[_cardIndex - 1].onNext?.call(_cardIndex - 1); // 重新绑定动画并启动 _setAnimation(); _progressController?.forward(); _playStory(isCallVisit: true); } else { // 当前故事组结束,切换到下一组 _pageController?.nextPage( duration: const Duration(milliseconds: 250), curve: Curves.easeOut, ).then((_) { cards[_cardIndex].onNext?.call(_cardIndex); // 重置卡片索引,启动下一组第一个故事的动画 _cardIndex = 0; _setAnimation(); _progressController?.forward(); _playStory(isCallVisit: true); }); } }
3. 调整动画容器逻辑
避免使用库传入的widget.animation,改用自定义的_animation防止冲突,同时优化进度条样式:
AnimatedBuilder( animation: _animation, builder: (context, child) { return LinearProgressIndicator( value: _animation.value, backgroundColor: Colors.grey.withOpacity(0.5), valueColor: const AlwaysStoppedAnimation<Color>(Colors.white), minHeight: 3, ); }, )
4. 添加快速跳过动画(可选)
如果需要点击跳过按钮时让进度条快速完成,可添加以下方法:
void _skipStory() { // 200ms内快速完成当前进度条动画,自动触发_nextStory _progressController?.animateTo(1.0, duration: const Duration(milliseconds: 200)); }
关键修复点说明
- 动画控制器必须设置有效时长,否则无法执行正常动画
- 切换故事前必须停止并重置当前动画,避免状态混乱
- 切换卡片索引后再启动新动画,防止重复播放当前故事
- 避免同时混用库的动画对象和自定义动画,防止逻辑冲突
内容的提问来源于stack exchange,提问作者Hasmukh Kachhatiya
相关产品推荐
相关产品推荐

