You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实现类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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 20:04:52