news 2026/5/12 16:47:29

Flutter for OpenHarmony音乐播放器App实战10:歌单列表实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Flutter for OpenHarmony音乐播放器App实战10:歌单列表实现

歌单列表页面展示各种分类的歌单,用户可以通过分类筛选找到感兴趣的歌单。我们使用分类标签栏配合网格布局实现这个页面。本篇将详细介绍如何实现一个功能完善的歌单列表页面。

功能分析

歌单列表页面需要实现以下功能:分类标签栏(全部、流行、摇滚、民谣等)、歌单网格展示、点击歌单进入详情页、分类切换时更新歌单列表。这个页面是用户发现新歌单的重要入口,设计上需要让用户能快速找到感兴趣的分类。

核心技术点

本篇涉及的核心技术包括:横向滚动的分类标签栏、GridView网格布局、状态管理(选中的分类)、页面导航传参、数据驱动的UI构建方式。

对应代码文件

lib/pages/playlist/playlist_list_page.dart

完整代码实现

import'package:flutter/material.dart';import'package:get/get.dart';import'playlist_detail_page.dart';

这段代码导入了Flutter核心库、GetX状态管理库以及歌单详情页面。GetX提供了简洁的路由导航API,方便我们进行页面跳转和参数传递。

classPlaylistListPageextendsStatefulWidget{constPlaylistListPage({super.key});@overrideState<PlaylistListPage>createState()=>_PlaylistListPageState();}

PlaylistListPage继承StatefulWidget,因为需要管理选中的分类状态。当用户点击不同分类时,页面需要更新显示对应分类的歌单列表。

class_PlaylistListPageStateextendsState<PlaylistListPage>{// 当前选中的分类String_selectedCategory='全部';// 分类列表finalList<String>_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:16),itemCount:_categories.length,itemBuilder:(context,index){finalcategory=_categories[index];finalisSelected=category==_selectedCategory;return_buildCategoryItem(category,isSelected);},),);}

分类标签栏固定高度50像素,使用横向滚动的ListView.builder实现。scrollDirection设为Axis.horizontal让列表横向滚动,这样可以支持更多分类而不会占用太多垂直空间。

/// 构建单个分类标签Widget_buildCategoryItem(Stringcategory,bool isSelected){returnGestureDetector(onTap:(){setState((){_selectedCategory=category;});},child:Container(margin:constEdgeInsets.only(right:12),padding:constEdgeInsets.symmetric(horizontal:16),alignment:Alignment.center,decoration:BoxDecoration(color:isSelected?constColor(0xFFE91E63):constColor(0xFF1E1E1E),borderRadius:BorderRadius.circular(20),),child:Text(category,style:TextStyle(color:isSelected?Colors.white:Colors.grey,fontSize:14,fontWeight:isSelected?FontWeight.w500:FontWeight.normal,),),),);}

GestureDetector处理点击事件,点击时调用setState更新选中的分类。Container使用条件表达式设置背景色,选中状态为粉色主题色,未选中为深灰色。BorderRadius.circular(20)让标签呈胶囊形状。

/// 构建歌单网格Widget_buildPlaylistGrid(){returnGridView.builder(padding:constEdgeInsets.all(16),gridDelegate:constSliverGridDelegateWithFixedCrossAxisCount(crossAxisCount:2,childAspectRatio:0.85,crossAxisSpacing:12,mainAxisSpacing:12,),itemCount:20,itemBuilder:(context,index){return_buildPlaylistItem(index);},);}

GridView.builder用于构建网格布局,采用懒加载方式只构建可见区域的子项。gridDelegate配置网格为2列,宽高比0.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:8),// 歌单标题_buildPlaylistTitle(index),],),);}

GestureDetector处理点击事件,通过Get.to导航到歌单详情页并传递歌单ID。Column垂直排列封面和标题,crossAxisAlignment设为start让内容左对齐。

/// 构建歌单封面Widget_buildPlaylistCover(int index){returnContainer(decoration:BoxDecoration(borderRadius:BorderRadius.circular(12),color:Colors.primaries[index%Colors.primaries.length].withOpacity(0.3),boxShadow:[BoxShadow(color:Colors.black.withOpacity(0.1),blurRadius:8,offset:constOffset(0,4),),],),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.5),borderRadius:BorderRadius.circular(10),),child:Row(mainAxisSize:MainAxisSize.min,children:[constIcon(Icons.play_arrow,color:Colors.white,size:12,),constSizedBox(width:2),Text('${(index+1)*10}万',style:constTextStyle(color:Colors.white,fontSize:10,),),],),),),],),);}

封面使用Container配合BoxDecoration实现圆角背景和阴影效果。Stack叠加封面图标和播放量标签,Positioned将播放量定位在右上角。播放量使用半透明黑色背景,让文字在任何颜色的封面上都清晰可见。

/// 构建歌单标题Widget_buildPlaylistTitle(int index){returnText('$_selectedCategory歌单${index+1}',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:16),itemCount:_categories.length,itemBuilder:(context,index){// 构建每个标签},)

scrollDirection设为Axis.horizontal是关键配置,让列表从左到右滚动。padding设置水平内边距,让第一个和最后一个标签不会紧贴屏幕边缘。

GridView网格布局说明

GridView.builder是创建网格列表的最佳选择,它采用懒加载方式只构建可见区域的子项:

// 网格布局配置详解GridView.builder(padding:constEdgeInsets.all(16),gridDelegate:constSliverGridDelegateWithFixedCrossAxisCount(crossAxisCount:2,// 每行显示2列childAspectRatio:0.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.3)

Colors.primaries是Flutter内置的主色调数组,包含红、粉、紫、蓝、青、绿、黄、橙等18种颜色。取模运算确保index超出数组长度时能循环使用颜色。

状态管理说明

页面使用StatefulWidget管理选中的分类状态:

// 状态更新onTap:(){setState((){_selectedCategory=category;});}

setState会触发build方法重新执行,UI会根据新的状态重新渲染。分类标签的颜色和歌单标题都会相应更新。

页面导航与传参

点击歌单时使用GetX进行页面导航:

// 页面导航Get.to(()=>PlaylistDetailPage(id:index))

Get.to是GetX提供的导航方法,比Navigator.push更简洁。通过构造函数传递歌单ID,详情页可以根据ID加载对应的歌单数据。

小结

本篇实现了音乐播放器的歌单列表页面。通过横向滚动的分类标签栏让用户快速筛选感兴趣的歌单类型,使用GridView网格布局展示歌单。setState管理选中状态,配合条件渲染实现UI的动态更新。封面上叠加播放量标签,让用户快速了解歌单的热度。这种设计模式在很多内容类App中都很常见。


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/12 7:00:02

Flutter for OpenHarmony音乐播放器App实战11:创建歌单实现

创建歌单是音乐播放器中一个基础但重要的功能。用户可以创建自己的歌单来整理和收藏喜欢的音乐。本篇文章将详细介绍如何实现一个简洁实用的创建歌单页面&#xff0c;包括封面上传、名称输入、隐私设置等功能。 页面基础结构 创建歌单页面使用StatefulWidget&#xff0c;因为…

作者头像 李华
网站建设 2026/5/11 11:51:01

基于Springboot公司资产管理系统【附源码+文档】

&#x1f495;&#x1f495;作者&#xff1a; 米罗学长 &#x1f495;&#x1f495;个人简介&#xff1a;混迹java圈十余年&#xff0c;精通Java、小程序、数据库等。 &#x1f495;&#x1f495;各类成品Java毕设 。javaweb&#xff0c;ssm&#xff0c;springboot等项目&#…

作者头像 李华
网站建设 2026/5/9 16:03:19

从金鱼记忆到博学大脑:构建AI Agent的专业检索系统全攻略

文章探讨了如何解决AI Agent的"金鱼记忆"问题&#xff0c;通过构建短期工作记忆和长期语义记忆两种核心能力。详细介绍了Agent记忆的两种实现方式、三级检索架构&#xff0c;以及性能优化方案&#xff08;Elasticsearch&#xff09;、语义召回方案&#xff08;Embedd…

作者头像 李华
网站建设 2026/5/12 7:42:44

一文掌握LoRA变体:分类理论到代码实现,解锁大模型高效微调新方法

本文首次对LoRA变体进行统一研究&#xff0c;提出基于秩、优化动力学、初始化策略和MoE集成的结构化分类体系&#xff0c;构建统一理论框架&#xff0c;推出LoRAFactory开源代码库实现50多种变体。大规模评估发现&#xff0c;LoRA对学习率高度敏感&#xff0c;且在适当配置下可…

作者头像 李华
网站建设 2026/5/9 10:21:25

一文读懂银行、上金所、基金公司最新政策全影响

央行工作会议强调延续适度宽松&#xff0c;黄金税收新政推高行业准入门槛&#xff0c;新基金发行市场科技主题当道——2026年的投资市场&#xff0c;在政策引导下正经历一场静水深流的深刻重塑。 中国人民银行宣布&#xff0c;2026年将继续实施好适度宽松的货币政策&#xff0c…

作者头像 李华