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

基于just_audio与Stream实现Flutter音频字幕高亮及自动滚动

自行开发音频字幕同步功能方案(基于just_audio: ^0.9.34)

1. 定义字幕数据结构

先统一字幕数据模型,不管是解析LRC文件还是自定义格式,都能适配:

class LyricLine {
  final Duration startTime; // 该行字幕开始显示时间
  final Duration endTime;   // 该行字幕结束显示时间
  final String text;        // 字幕文本

  LyricLine({
    required this.startTime,
    required this.endTime,
    required this.text,
  });
}

如果用LRC格式,自己写个解析函数即可——把每行[00:12.34]歌词内容拆成时间戳和文本,转成LyricLine对象存入列表。

2. 监听音频进度匹配当前字幕

利用just_audio的player.position流监听实时播放进度,实时匹配对应字幕:

  • 用ValueNotifier托管当前高亮字幕的索引,避免频繁调用setState刷新UI
  • 进度更新时,找到startTime ≤ 当前进度 ≤ endTime的字幕行,更新索引;字幕数量多的话,改用二分查找提升性能

示例代码:

final player = AudioPlayer();
final ValueNotifier<int> currentLyricIndex = ValueNotifier(-1);
final List<LyricLine> lyricLines = []; // 提前解析好的字幕列表

// 监听播放进度,匹配对应字幕
player.position.listen((position) {
  // 这里用遍历示例,字幕多的话替换成二分查找
  for (int i = 0; i < lyricLines.length; i++) {
    final line = lyricLines[i];
    if (position >= line.startTime && position <= line.endTime) {
      if (currentLyricIndex.value != i) {
        currentLyricIndex.value = i;
      }
      break;
    }
  }
});

3. 渲染字幕列表并高亮当前行

用ListView.builder渲染所有字幕,通过ValueListenableBuilder监听索引变化,高亮当前行:

  • 给当前行设置差异化样式(字体大小、颜色、粗细)
  • 加动画过渡,让高亮切换更自然

示例代码:

ValueListenableBuilder<int>(
  valueListenable: currentLyricIndex,
  builder: (context, activeIndex, child) {
    return ListView.builder(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
      itemCount: lyricLines.length,
      itemBuilder: (context, i) {
        final line = lyricLines[i];
        final isCurrent = i == activeIndex;
        return AnimatedContainer(
          duration: const Duration(milliseconds: 300),
          padding: const EdgeInsets.symmetric(vertical: 8),
          child: Text(
            line.text,
            textAlign: TextAlign.center,
            style: TextStyle(
              fontSize: isCurrent ? 20 : 16,
              color: isCurrent ? Colors.blue : Colors.grey,
              fontWeight: isCurrent ? FontWeight.bold : FontWeight.normal,
            ),
          ),
        );
      },
    );
  },
)

4. 实现自动滚动到当前字幕

给ListView绑定ScrollController,当当前字幕索引变化时触发自动滚动:

  • 计算当前行的滚动偏移量(可假设每行固定高度,或通过RenderObject获取实际高度)
  • 用animateTo实现平滑滚动,避免生硬跳转

示例代码:

final ScrollController scrollController = ScrollController();

// 监听索引变化触发滚动
currentLyricIndex.addListener(() {
  final activeIndex = currentLyricIndex.value;
  if (activeIndex == -1) return;
  
  // 假设每行高度40,计算偏移量;要精确高度可通过RenderBox获取
  final offset = activeIndex * 40.0;
  scrollController.animateTo(
    offset,
    duration: const Duration(milliseconds: 500),
    curve: Curves.easeInOut,
  );
});

// 把controller绑定到ListView
ListView.builder(
  controller: scrollController,
  // ...其他参数
)

5. 优化同步精度与体验

  • 加100ms容错:判断position >= line.startTime - const Duration(milliseconds: 100),避免音频进度抖动导致字幕频繁切换
  • 处理拖动进度:音频seek后立即触发字幕匹配,无需等待position流更新
  • 兼容异常LRC格式:没有结束时间的行,自动用下一行的开始时间作为当前行的结束时间

6. 自定义扩展(按需添加)

  • 点击字幕跳转时间:给列表项加GestureDetector,点击时调用player.seek(line.startTime)
  • 双语字幕展示:修改LyricLine添加translation字段,列表项同时显示原文和翻译
  • 可配置样式:把字体、颜色、行间距做成参数,方便组件复用

内容的提问来源于stack exchange,提问作者Jinto Joseph

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 09:13:21