Flutter for OpenHarmony 音乐播放器App实战25 - 歌词显示实现
本文介绍了一个音乐播放器歌词显示页面的实现方案。页面采用渐变背景设计,主要功能包括:顶部显示歌曲信息、可滚动的歌词列表(当前行高亮显示)、支持点击跳转播放以及底部播放控制。技术实现上使用Flutter框架,通过StatefulWidget管理状态,ListView.builder构建歌词列表,ScrollController控制滚动位置,Slider组件实现进度条。当前播放行通过改变字体大小、颜色
前言
歌词显示是音乐播放器中非常受欢迎的功能,用户可以边听歌边看歌词,跟着一起唱。本篇我们来实现一个完整的歌词显示页面,包含歌词滚动列表、当前行高亮、点击跳转、以及底部播放控制。整个页面采用渐变背景设计,营造沉浸式的听歌体验。
功能分析
歌词页面需要实现以下功能:顶部显示歌曲名和歌手名,中间是可滚动的歌词列表,当前播放的歌词行高亮显示,用户可以点击任意歌词行跳转播放,底部提供进度条和播放控制按钮。
页面框架搭建
歌词页面需要管理当前播放行和滚动控制器,使用 StatefulWidget 实现:
class LyricsPage extends StatefulWidget {
const LyricsPage({super.key});
State<LyricsPage> createState() => _LyricsPageState();
}
class _LyricsPageState extends State<LyricsPage> {
int currentLine = 5;
final ScrollController _scrollController = ScrollController();
currentLine 记录当前播放到第几行歌词,默认值为5表示从第6行开始(索引从0开始)。_scrollController 用于控制歌词列表的滚动位置,实现歌词自动滚动到当前行。
歌词数据结构
歌词数据使用 List<Map> 存储,每条歌词包含时间戳和文本内容:
final List<Map<String, dynamic>> lyrics = [
{'time': 0, 'text': '作词: 音乐人'},
{'time': 1, 'text': '作曲: 音乐人'},
{'time': 5, 'text': ''},
{'time': 10, 'text': '夜空中最亮的星'},
{'time': 15, 'text': '能否听清'},
{'time': 20, 'text': '那仰望的人'},
{'time': 25, 'text': '心底的孤独和叹息'},
{'time': 30, 'text': ''},
{'time': 35, 'text': '夜空中最亮的星'},
{'time': 40, 'text': '能否记起'},
{'time': 45, 'text': '曾与我同行'},
{'time': 50, 'text': '消失在风里的身影'},
{'time': 55, 'text': ''},
{'time': 60, 'text': '我祈祷拥有一颗透明的心灵'},
{'time': 65, 'text': '和会流泪的眼睛'},
{'time': 70, 'text': '给我再去相信的勇气'},
{'time': 75, 'text': '越过谎言去拥抱你'},
];
time 字段表示这行歌词对应的播放时间(秒),text 字段是歌词内容。空字符串表示歌词间的空行,用于视觉上的分隔。实际项目中这些数据通常从LRC文件解析而来。
页面主体结构
页面使用 Stack 叠加渐变背景和内容区域:
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Stack(
children: [
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
const Color(0xFFE91E63).withOpacity(0.3),
Colors.black
]
)
)
),
SafeArea(
child: Column(
children: [
_buildHeader(),
Expanded(child: _buildLyrics()),
_buildControls(),
],
),
),
],
),
);
}
Stack 的第一层是渐变背景,从顶部的粉色渐变到底部的黑色。第二层是实际内容,使用 Column 布局分为三部分:顶部标题栏、中间歌词列表、底部播放控制。SafeArea 确保内容不会被状态栏遮挡。
顶部标题栏
标题栏显示歌曲名、歌手名,以及返回和分享按钮:
Widget _buildHeader() {
return Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
IconButton(
icon: const Icon(Icons.keyboard_arrow_down, size: 30),
onPressed: () => Get.back()
),
const Expanded(
child: Column(
children: [
Text('夜空中最亮的星',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
SizedBox(height: 4),
Text('逃跑计划', style: TextStyle(color: Colors.grey)),
],
),
),
IconButton(icon: const Icon(Icons.share), onPressed: () {}),
],
),
);
}
左侧是向下箭头按钮,点击返回上一页。中间使用 Expanded 占据剩余空间,显示歌曲名和歌手名,歌曲名使用粗体突出显示。右侧是分享按钮。这种布局是音乐App中常见的设计模式。
歌词列表实现
歌词列表是页面的核心部分,使用 ListView.builder 构建:
Widget _buildLyrics() {
return ListView.builder(
controller: _scrollController,
padding: const EdgeInsets.symmetric(vertical: 100),
itemCount: lyrics.length,
itemBuilder: (context, index) {
final isCurrentLine = index == currentLine;
return GestureDetector(
onTap: () => setState(() => currentLine = index),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 32),
child: Text(
lyrics[index]['text'],
textAlign: TextAlign.center,
style: TextStyle(
fontSize: isCurrentLine ? 20 : 16,
fontWeight: isCurrentLine ? FontWeight.bold : FontWeight.normal,
color: isCurrentLine
? const Color(0xFFE91E63)
: Colors.white.withOpacity(0.5),
),
),
),
);
},
);
}
padding: const EdgeInsets.symmetric(vertical: 100) 在列表顶部和底部添加100像素的空白,让歌词可以滚动到屏幕中央位置。
每行歌词用 GestureDetector 包裹,点击后更新 currentLine 实现跳转功能。当前行使用更大的字号(20px vs 16px)、粗体和粉色高亮显示,其他行使用半透明白色,形成明显的视觉对比。
底部播放控制
底部控制区包含进度条和播放按钮:
Widget _buildControls() {
return Container(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Row(
children: [
const Text('2:35',
style: TextStyle(color: Colors.grey, fontSize: 12)),
Expanded(
child: Slider(
value: 0.4,
activeColor: const Color(0xFFE91E63),
inactiveColor: Colors.grey.withOpacity(0.3),
onChanged: (v) {}
)
),
const Text('4:28',
style: TextStyle(color: Colors.grey, fontSize: 12)),
],
),
进度条使用 Slider 组件,左右两侧分别显示当前播放时间和总时长。activeColor 设置已播放部分的颜色为粉色,inactiveColor 设置未播放部分为灰色。
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(icon: const Icon(Icons.shuffle), onPressed: () {}),
IconButton(
icon: const Icon(Icons.skip_previous, size: 36),
onPressed: () {}
),
Container(
width: 64, height: 64,
decoration: const BoxDecoration(
color: Color(0xFFE91E63),
shape: BoxShape.circle
),
child: IconButton(
icon: const Icon(Icons.pause, size: 32, color: Colors.white),
onPressed: () {}
),
),
IconButton(
icon: const Icon(Icons.skip_next, size: 36),
onPressed: () {}
),
IconButton(icon: const Icon(Icons.repeat), onPressed: () {}),
],
),
],
),
);
}
播放控制按钮使用 Row 水平排列,spaceEvenly 让按钮均匀分布。中间的播放/暂停按钮使用粉色圆形背景突出显示,是整个控制区的视觉焦点。上一首和下一首按钮使用较大的图标(36px),随机播放和循环播放按钮使用默认大小。
页面入口
歌词页面的入口在播放器页面,用户点击"点击查看歌词"文字即可进入:
GestureDetector(
onTap: () => Get.to(() => const LyricsPage()),
child: const Text('点击查看歌词', style: TextStyle(color: Colors.white54))
),
也可以在播放器页面的进度条区域添加这个入口,让用户更容易发现歌词功能。
扩展思路
实际项目中,歌词同步需要与播放器的播放进度联动。可以监听播放器的进度变化,根据当前播放时间找到对应的歌词行,然后更新 currentLine 并调用 _scrollController.animateTo() 平滑滚动到该行。
点击歌词跳转功能需要调用播放器的 seek 方法,将播放位置跳转到对应歌词的时间点。
还可以添加歌词翻译功能,在原歌词下方显示翻译文本,或者添加歌词字体大小调节、歌词背景模糊等个性化设置。
小结
本篇我们实现了一个功能完整的歌词显示页面,包含渐变背景、歌词列表、当前行高亮、点击跳转和播放控制。通过 ListView.builder 构建歌词列表,使用条件样式实现当前行高亮效果。渐变背景和统一的粉色主题色让页面视觉效果更加协调。在实际项目中,还需要与播放器进度联动,实现真正的歌词同步滚动功能。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐


所有评论(0)