前言
歌词显示是音乐播放器中非常受欢迎的功能,用户可以边听歌边看歌词,跟着一起唱。本篇我们来实现一个完整的歌词显示页面,包含歌词滚动列表、当前行高亮、点击跳转、以及底部播放控制。整个页面采用渐变背景设计,营造沉浸式的听歌体验。
功能分析
歌词页面需要实现以下功能:顶部显示歌曲名和歌手名,中间是可滚动的歌词列表,当前播放的歌词行高亮显示,用户可以点击任意歌词行跳转播放,底部提供进度条和播放控制按钮。
页面框架搭建
歌词页面需要管理当前播放行和滚动控制器,使用StatefulWidget实现:
classLyricsPageextendsStatefulWidget{constLyricsPage({super.key});@overrideState<LyricsPage>createState()=>_LyricsPageState();}class_LyricsPageStateextendsState<LyricsPage>{int currentLine=5;finalScrollController_scrollController=ScrollController();currentLine记录当前播放到第几行歌词,默认值为5表示从第6行开始(索引从0开始)。_scrollController用于控制歌词列表的滚动位置,实现歌词自动滚动到当前行。
歌词数据结构
歌词数据使用List<Map>存储,每条歌词包含时间戳和文本内容:
finalList<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叠加渐变背景和内容区域:
@overrideWidgetbuild(BuildContextcontext){returnScaffold(backgroundColor:Colors.black,body:Stack(children:[Container(decoration:BoxDecoration(gradient:LinearGradient(begin:Alignment.topCenter,end:Alignment.bottomCenter,colors:[constColor(0xFFE91E63).withOpacity(0.3),Colors.black]))),SafeArea(child:Column(children:[_buildHeader(),Expanded(child:_buildLyrics()),_buildControls(),],),),],),);}Stack的第一层是渐变背景,从顶部的粉色渐变到底部的黑色。第二层是实际内容,使用Column布局分为三部分:顶部标题栏、中间歌词列表、底部播放控制。SafeArea确保内容不会被状态栏遮挡。
顶部标题栏
标题栏显示歌曲名、歌手名,以及返回和分享按钮:
Widget_buildHeader(){returnPadding(padding:constEdgeInsets.all(16),child:Row(children:[IconButton(icon:constIcon(Icons.keyboard_arrow_down,size:30),onPressed:()=>Get.back()),constExpanded(child:Column(children:[Text('夜空中最亮的星',style:TextStyle(fontSize:18,fontWeight:FontWeight.bold)),SizedBox(height:4),Text('逃跑计划',style:TextStyle(color:Colors.grey)),],),),IconButton(icon:constIcon(Icons.share),onPressed:(){}),],),);}左侧是向下箭头按钮,点击返回上一页。中间使用Expanded占据剩余空间,显示歌曲名和歌手名,歌曲名使用粗体突出显示。右侧是分享按钮。这种布局是音乐App中常见的设计模式。
歌词列表实现
歌词列表是页面的核心部分,使用ListView.builder构建:
Widget_buildLyrics(){returnListView.builder(controller:_scrollController,padding:constEdgeInsets.symmetric(vertical:100),itemCount:lyrics.length,itemBuilder:(context,index){finalisCurrentLine=index==currentLine;returnGestureDetector(onTap:()=>setState(()=>currentLine=index),child:Container(padding:constEdgeInsets.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?constColor(0xFFE91E63):Colors.white.withOpacity(0.5),),),),);},);}padding: const EdgeInsets.symmetric(vertical: 100)在列表顶部和底部添加100像素的空白,让歌词可以滚动到屏幕中央位置。
每行歌词用GestureDetector包裹,点击后更新currentLine实现跳转功能。当前行使用更大的字号(20px vs 16px)、粗体和粉色高亮显示,其他行使用半透明白色,形成明显的视觉对比。
底部播放控制
底部控制区包含进度条和播放按钮:
Widget_buildControls(){returnContainer(padding:constEdgeInsets.all(20),child:Column(children:[Row(children:[constText('2:35',style:TextStyle(color:Colors.grey,fontSize:12)),Expanded(child:Slider(value:0.4,activeColor:constColor(0xFFE91E63),inactiveColor:Colors.grey.withOpacity(0.3),onChanged:(v){})),constText('4:28',style:TextStyle(color:Colors.grey,fontSize:12)),],),进度条使用Slider组件,左右两侧分别显示当前播放时间和总时长。activeColor设置已播放部分的颜色为粉色,inactiveColor设置未播放部分为灰色。
Row(mainAxisAlignment:MainAxisAlignment.spaceEvenly,children:[IconButton(icon:constIcon(Icons.shuffle),onPressed:(){}),IconButton(icon:constIcon(Icons.skip_previous,size:36),onPressed:(){}),Container(width:64,height:64,decoration:constBoxDecoration(color:Color(0xFFE91E63),shape:BoxShape.circle),child:IconButton(icon:constIcon(Icons.pause,size:32,color:Colors.white),onPressed:(){}),),IconButton(icon:constIcon(Icons.skip_next,size:36),onPressed:(){}),IconButton(icon:constIcon(Icons.repeat),onPressed:(){}),],),],),);}播放控制按钮使用Row水平排列,spaceEvenly让按钮均匀分布。中间的播放/暂停按钮使用粉色圆形背景突出显示,是整个控制区的视觉焦点。上一首和下一首按钮使用较大的图标(36px),随机播放和循环播放按钮使用默认大小。
页面入口
歌词页面的入口在播放器页面,用户点击"点击查看歌词"文字即可进入:
GestureDetector(onTap:()=>Get.to(()=>constLyricsPage()),child:constText('点击查看歌词',style:TextStyle(color:Colors.white54))),也可以在播放器页面的进度条区域添加这个入口,让用户更容易发现歌词功能。
扩展思路
实际项目中,歌词同步需要与播放器的播放进度联动。可以监听播放器的进度变化,根据当前播放时间找到对应的歌词行,然后更新currentLine并调用_scrollController.animateTo()平滑滚动到该行。
点击歌词跳转功能需要调用播放器的seek方法,将播放位置跳转到对应歌词的时间点。
还可以添加歌词翻译功能,在原歌词下方显示翻译文本,或者添加歌词字体大小调节、歌词背景模糊等个性化设置。
小结
本篇我们实现了一个功能完整的歌词显示页面,包含渐变背景、歌词列表、当前行高亮、点击跳转和播放控制。通过ListView.builder构建歌词列表,使用条件样式实现当前行高亮效果。渐变背景和统一的粉色主题色让页面视觉效果更加协调。在实际项目中,还需要与播放器进度联动,实现真正的歌词同步滚动功能。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net