flutter如何实现歌词滚动显示

在Flutter中如何实现类似音乐播放器的歌词滚动显示效果?目前需要实现以下功能:1. 根据当前播放时间自动滚动到对应的歌词行;2. 高亮显示当前播放的歌词;3. 支持上下滑动查看歌词。请问有没有推荐的实现方案或现成的插件可以使用?最好能提供一些示例代码或实现思路。

2 回复

Flutter中实现歌词滚动显示,可以这样做:

  1. 解析歌词:将LRC格式歌词解析为时间戳和歌词内容的列表

  2. 使用ListView.builder

ListView.builder(
  itemCount: lyrics.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(lyrics[index].text),
      // 高亮当前播放的歌词行
      tileColor: index == currentIndex ? Colors.blue : null,
    );
  },
)
  1. 滚动控制
// 滚动到指定位置
_scrollController.animateTo(
  offset,
  duration: Duration(milliseconds: 500),
  curve: Curves.easeInOut,
);
  1. 定时器监听:通过Timer或AnimationController监听播放进度,更新currentIndex并触发滚动

  2. 优化:可添加歌词缓存、平滑滚动动画、字体大小变化等效果

核心思路是监听播放时间,匹配对应歌词索引,然后控制ListView滚动到相应位置。

更多关于flutter如何实现歌词滚动显示的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中实现歌词滚动显示,可以通过以下步骤完成:

  1. 解析歌词文件:将LRC格式的歌词解析为时间戳和歌词文本的列表。
  2. 监听音频播放进度:使用音频播放插件(如audioplayers)获取当前播放时间。
  3. 高亮当前歌词:根据当前时间匹配对应的歌词行,并高亮显示。
  4. 自动滚动:使用ScrollControllerListViewscrollToIndex方法实现滚动效果。

示例代码

1. 歌词数据模型

class Lyric {
  final Duration time;
  final String text;

  Lyric(this.time, this.text);
}

2. 解析歌词

List<Lyric> parseLyrics(String lrcContent) {
  final lines = lrcContent.split('\n');
  final List<Lyric> lyrics = [];
  final RegExp regex = RegExp(r'\[(\d+):(\d+).(\d+)\](.*)');

  for (var line in lines) {
    final match = regex.firstMatch(line);
    if (match != null) {
      final min = int.parse(match.group(1)!);
      final sec = int.parse(match.group(2)!);
      final hundred = int.parse(match.group(3)!);
      final text = match.group(4)!.trim();
      
      final duration = Duration(
        minutes: min,
        seconds: sec,
        milliseconds: hundred * 10,
      );
      lyrics.add(Lyric(duration, text));
    }
  }
  return lyrics;
}

3. 实现滚动歌词组件

class LyricWidget extends StatefulWidget {
  final List<Lyric> lyrics;
  final Duration currentTime;

  const LyricWidget({
    Key? key,
    required this.lyrics,
    required this.currentTime,
  }) : super(key: key);

  @override
  _LyricWidgetState createState() => _LyricWidgetState();
}

class _LyricWidgetState extends State<LyricWidget> {
  final ScrollController _scrollController = ScrollController();
  int _currentIndex = 0;

  @override
  void didUpdateWidget(covariant LyricWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    _updateLyricIndex();
  }

  void _updateLyricIndex() {
    for (int i = 0; i < widget.lyrics.length; i++) {
      if (widget.currentTime >= widget.lyrics[i].time &&
          (i == widget.lyrics.length - 1 || 
           widget.currentTime < widget.lyrics[i + 1].time)) {
        if (_currentIndex != i) {
          setState(() {
            _currentIndex = i;
          });
          _scrollToCurrentLyric();
        }
        break;
      }
    }
  }

  void _scrollToCurrentLyric() {
    final context = _scrollController.position.context;
    if (context != null) {
      _scrollController.animateTo(
        _currentIndex * 50.0, // 假设每行高度为50
        duration: const Duration(milliseconds: 300),
        curve: Curves.easeInOut,
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      controller: _scrollController,
      itemCount: widget.lyrics.length,
      itemBuilder: (context, index) {
        final lyric = widget.lyrics[index];
        return Container(
          padding: const EdgeInsets.symmetric(vertical: 8),
          alignment: Alignment.center,
          child: Text(
            lyric.text,
            style: TextStyle(
              fontSize: _currentIndex == index ? 20 : 16,
              color: _currentIndex == index ? Colors.blue : Colors.grey,
              fontWeight: _currentIndex == index ? FontWeight.bold : FontWeight.normal,
            ),
          ),
        );
      },
    );
  }
}

使用方式

LyricWidget(
  lyrics: parseLyrics(lrcString),
  currentTime: currentAudioPosition, // 从音频播放器获取
)

说明

  • 歌词解析:支持标准LRC格式,提取时间戳和歌词文本。
  • 滚动控制:通过ScrollController实现平滑滚动到当前歌词。
  • 样式高亮:当前播放的歌词会放大并改变颜色,其他歌词显示为灰色。

如果需要更精确的滚动定位,可以考虑使用scrollable_positioned_list包替代默认的ListView。

回到顶部