核心内容摘要
瑶妹脸红流眼泪:一个少女心事的回眸_1
歌单列表页面展示各种分类的歌单用户可以通过分类筛选找到感兴趣的歌单。
我们使用分类标签栏配合网格布局实现这个页面。
本篇将详细介绍如何实现一个功能完善的歌单列表页面。
功能分析歌单列表页面需要实现以下功能分类标签栏全部、流行、摇滚、民谣等、歌单网格展示、点击歌单进入详情页、分类切换时更新歌单列表。
这个页面是用户发现新歌单的重要入口设计上需要让用户能快速找到感兴趣的分类。
核心技术点本篇涉及的核心技术包括横向滚动的分类标签栏、GridView网格布局、状态管理选中的分类、页面导航传参、数据驱动的UI构建方式。
对应代码文件lib/pages/playlist/playlist_list_page.dart完整代码实现importpackage:flutter/material.dart;importpackage:get/get.dart;importplaylist_detail_page.dart;这段代码导入了Flutter核心库、GetX状态管理库以及歌单详情页面。
GetX提供了简洁的路由导航API方便我们进行页面跳转和参数传递。
classPlaylistListPageextendsStatefulWidget{constPlaylistListPage({super.key});overrideStatePlaylistListPagecreateState()_PlaylistListPageState();}PlaylistListPage继承StatefulWidget因为需要管理选中的分类状态。
当用户点击不同分类时页面需要更新显示对应分类的歌单列表。
class_PlaylistListPageStateextendsStatePlaylistListPage{// 当前选中的分类String_selectedCategory全部;// 分类列表finalListString_categories[全部,流行,摇滚,民谣,电子,古典,爵士,];_selectedCategory存储当前选中的分类默认是全部。
_categories是分类列表包含了常见的音乐风格分类。
实际项目中这个列表可能从服务器获取。
overrideWidgetbuild(BuildContextcontext){returnScaffold(appBar:AppBar(title:constText(歌单广场),elevation:0,),body:Column(children:[// 分类标签栏_buildCategoryBar(),// 歌单网格Expanded(child:_buildPlaylistGrid(),),],),);}build方法构建页面UI。
Scaffold提供基础页面结构AppBar显示歌单广场标题。
页面使用Column垂直排列分类标签栏和歌单网格Expanded让网格占据剩余空间。
/// 构建分类标签栏Widget_buildCategoryBar(){returnSizedBox(height:50,child:ListView.builder(scrollDirection:Axis.horizontal,padding:constEdgeInsets.symmetric(horizontal:
,itemCount:_categories.length,itemBuilder:(context,index){finalcategory_categories[index];finalisSelectedcategory_selectedCategory;return_buildCategoryItem(category,isSelected);},),);}分类标签栏固定高度50像素使用横向滚动的ListView.builder实现。
scrollDirection设为Axis.horizontal让列表横向滚动这样可以支持更多分类而不会占用太多垂直空间。
/// 构建单个分类标签Widget_buildCategoryItem(Stringcategory,bool isSelected){returnGestureDetector(onTap:(){setState((){_selectedCategorycategory;});},child:Container(margin:constEdgeInsets.only(right:
,padding:constEdgeInsets.symmetric(horizontal:
,alignment:Alignment.center,decoration:BoxDecoration(color:isSelected?constColor(0xFFE91E
:constColor(0xFF1E1E1E),borderRadius:BorderRadius.circular(
,),child:Text(category,style:TextStyle(color:isSelected?Colors.white:Colors.grey,fontSize:14,fontWeight:isSelected?FontWeight.w500:FontWeight.normal,),),),);}GestureDetector处理点击事件点击时调用setState更新选中的分类。
Container使用条件表达式设置背景色选中状态为粉色主题色未选中为深灰色。
BorderRadius.circular(
让标签呈胶囊形状。
/// 构建歌单网格Widget_buildPlaylistGrid(){returnGridView.builder(padding:constEdgeInsets.all(
,gridDelegate:constSliverGridDelegateWithFixedCrossAxisCount(crossAxisCount:2,childAspectRatio:
85,crossAxisSpacing:12,mainAxisSpacing:12,),itemCount:20,itemBuilder:(context,index){return_buildPlaylistItem(index);},);}GridView.builder用于构建网格布局采用懒加载方式只构建可见区域的子项。
gridDelegate配置网格为2列宽高比
85间距12像素。
itemCount设为20表示显示20个歌单。
/// 构建单个歌单项Widget_buildPlaylistItem(int index){returnGestureDetector(onTap:(){Get.to(()PlaylistDetailPage(id:index));},child:Column(crossAxisAlignment:CrossAxisAlignment.start,children:[// 歌单封面Expanded(child:_buildPlaylistCover(index),),constSizedBox(height:
,// 歌单标题_buildPlaylistTitle(index),],),);}GestureDetector处理点击事件通过Get.to导航到歌单详情页并传递歌单ID。
Column垂直排列封面和标题crossAxisAlignment设为start让内容左对齐。
/// 构建歌单封面Widget_buildPlaylistCover(int index){returnContainer(decoration:BoxDecoration(borderRadius:BorderRadius.circular(
,color:Colors.primaries[index%Colors.primaries.length].withOpacity(
0.
,boxShadow:[BoxShadow(color:Colors.black.withOpacity(
0.
,blurRadius:8,offset:constOffset(0,
,),],),child:Stack(children:[// 封面图标constCenter(child:Icon(Icons.queue_music,size:50,color:Colors.white70,),),// 播放量Positioned(right:8,top:8,child:Container(padding:constEdgeInsets.symmetric(horizontal:6,vertical:2,),decoration:BoxDecoration(color:Colors.black.withOpacity(
0.
,borderRadius:BorderRadius.circular(
,),child:Row(mainAxisSize:MainAxisSize.min,children:[constIcon(Icons.play_arrow,color:Colors.white,size:12,),constSizedBox(width:
,Text(${(index
*10}万,style:constTextStyle(color:Colors.white,fontSize:10,),),],),),),],),);}封面使用Container配合BoxDecoration实现圆角背景和阴影效果。
Stack叠加封面图标和播放量标签Positioned将播放量定位在右上角。
播放量使用半透明黑色背景让文字在任何颜色的封面上都清晰可见。
/// 构建歌单标题Widget_buildPlaylistTitle(int index){returnText($_selectedCategory歌单${index1},style:constTextStyle(fontSize:14,fontWeight:FontWeight.w400,),maxLines:2,overflow:TextOverflow.ellipsis,);}}歌单标题中包含当前选中的分类名称切换分类时标题也会相应变化。
maxLines限制最多显示两行overflow设置溢出时显示省略号保证界面整洁。
分类标签栏实现详解分类标签使用横向滚动的ListView.builder实现这是处理横向列表的标准方式// 横向滚动列表的关键配置ListView.builder(scrollDirection:Axis.horizontal,// 设置为横向滚动padding:constEdgeInsets.symmetric(horizontal:
,itemCount:_categories.length,itemBuilder:(context,index){// 构建每个标签},)scrollDirection设为Axis.horizontal是关键配置让列表从左到右滚动。
padding设置水平内边距让第一个和最后一个标签不会紧贴屏幕边缘。
GridView网格布局说明GridView.builder是创建网格列表的最佳选择它采用懒加载方式只构建可见区域的子项// 网格布局配置详解GridView.builder(padding:constEdgeInsets.all(
,gridDelegate:constSliverGridDelegateWithFixedCrossAxisCount(crossAxisCount:2,// 每行显示2列childAspectRatio:
85,// 子项宽高比crossAxisSpacing:12,// 列间距mainAxisSpacing:12,// 行间距),itemCount:20,itemBuilder:(context,index){returnbuildItem(index);},)SliverGridDelegateWithFixedCrossAxisCount定义网格布局规则crossAxisCount设置列数childAspectRatio设置宽高比宽度/高度小于1表示高度大于宽度。
动态颜色分配封面使用Colors.primaries数组中的颜色通过取模运算让每个歌单有不同的颜色// 动态颜色分配color:Colors.primaries[index%Colors.primaries.length].withOpacity(
0.
Colors.primaries是Flutter内置的主色调数组包含红、粉、紫、蓝、青、绿、黄、橙等18种颜色。
取模运算确保index超出数组长度时能循环使用颜色。
状态管理说明页面使用StatefulWidget管理选中的分类状态// 状态更新onTap:(){setState((){_selectedCategorycategory;});}setState会触发build方法重新执行UI会根据新的状态重新渲染。
分类标签的颜色和歌单标题都会相应更新。
页面导航与传参点击歌单时使用GetX进行页面导航// 页面导航Get.to(()PlaylistDetailPage(id:index))Get.to是GetX提供的导航方法比Navigator.push更简洁。
通过构造函数传递歌单ID详情页可以根据ID加载对应的歌单数据。
小结本篇实现了音乐播放器的歌单列表页面。
通过横向滚动的分类标签栏让用户快速筛选感兴趣的歌单类型使用GridView网格布局展示歌单。
setState管理选中状态配合条件渲染实现UI的动态更新。
封面上叠加播放量标签让用户快速了解歌单的热度。
这种设计模式在很多内容类App中都很常见。
欢迎加入开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.net