news 2026/6/15 12:48:45

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

作者头像

张小明

前端开发工程师

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

歌手列表页面展示所有歌手,用户可以浏览并点击进入歌手详情页。本篇我们使用网格布局来实现这个页面,每个歌手显示圆形头像和名称。这是音乐App中常见的歌手展示方式。

功能分析

歌手列表页面需要实现以下功能:网格布局展示歌手、圆形头像显示、点击歌手进入详情页、歌手分类筛选。这个页面是用户发现新歌手的重要入口,设计上需要让用户能快速浏览和找到感兴趣的歌手。

核心技术点

本篇涉及的核心技术包括:GridView.builder网格布局、CircleAvatar圆形头像、GestureDetector点击事件、GetX路由导航、动态颜色分配、分类筛选功能。

对应代码文件

lib/pages/artist/artist_list_page.dart

完整代码实现

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

这段代码导入了Flutter核心库、GetX状态管理库以及歌手详情页面。歌手列表需要跳转到歌手详情页,因此需要导入详情页的引用。

classArtistListPageextendsStatefulWidget{constArtistListPage({super.key});@overrideState<ArtistListPage>createState()=>_ArtistListPageState();}

ArtistListPage继承StatefulWidget,因为我们需要管理歌手分类的选中状态。当用户切换分类时,页面需要更新显示对应分类的歌手。

class_ArtistListPageStateextendsState<ArtistListPage>{// 当前选中的分类String_selectedType='全部';// 歌手类型列表finalList<String>_artistTypes=['全部','华语男','华语女','欧美男','欧美女','组合','乐队',];

_selectedType存储当前选中的歌手类型,默认是"全部"。_artistTypes是歌手类型列表,包含了常见的歌手分类方式,方便用户快速筛选。

@overrideWidgetbuild(BuildContextcontext){returnScaffold(appBar:AppBar(title:constText('歌手'),elevation:0,),body:Column(children:[// 分类筛选栏_buildFilterBar(),// 歌手网格Expanded(child:_buildArtistGrid(),),],),);}

build方法构建页面UI。Scaffold提供基础页面结构,AppBar显示"歌手"标题。页面使用Column垂直排列分类筛选栏和歌手网格,Expanded让网格占据剩余空间。

/// 构建分类筛选栏Widget_buildFilterBar(){returnSizedBox(height:50,child:ListView.builder(scrollDirection:Axis.horizontal,padding:constEdgeInsets.symmetric(horizontal:16),itemCount:_artistTypes.length,itemBuilder:(context,index){finaltype=_artistTypes[index];finalisSelected=type==_selectedType;return_buildFilterItem(type,isSelected);},),);}

分类筛选栏固定高度50像素,使用横向滚动的ListView.builder实现。这样可以支持更多分类选项而不会占用太多垂直空间,用户可以左右滑动查看所有分类。

/// 构建单个筛选项Widget_buildFilterItem(Stringtype,bool isSelected){returnGestureDetector(onTap:(){setState((){_selectedType=type;});},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(type,style:TextStyle(color:isSelected?Colors.white:Colors.grey,fontSize:14,),),),);}

GestureDetector处理点击事件,点击时调用setState更新选中的分类。Container使用条件表达式设置背景色,选中状态为粉色主题色,未选中为深灰色。圆角设为20像素呈胶囊形状。

/// 构建歌手网格Widget_buildArtistGrid(){returnGridView.builder(padding:constEdgeInsets.all(16),gridDelegate:constSliverGridDelegateWithFixedCrossAxisCount(crossAxisCount:3,childAspectRatio:0.8,crossAxisSpacing:16,mainAxisSpacing:16,),itemCount:30,itemBuilder:(context,index){return_buildArtistItem(index);},);}

GridView.builder用于构建网格布局,采用懒加载方式只构建可见区域的子项。gridDelegate配置网格为3列,宽高比0.8,间距16像素。每行显示3个歌手,布局紧凑又不拥挤。

/// 构建单个歌手项Widget_buildArtistItem(int index){returnGestureDetector(onTap:(){Get.to(()=>ArtistDetailPage(id:index));},child:Column(children:[// 歌手头像Expanded(child:_buildArtistAvatar(index),),constSizedBox(height:8),// 歌手名称_buildArtistName(index),],),);}

GestureDetector处理点击事件,通过Get.to导航到歌手详情页并传递歌手ID。Column垂直排列头像和名称,Expanded让头像区域占据剩余空间。

/// 构建歌手头像Widget_buildArtistAvatar(int index){returnContainer(decoration:BoxDecoration(shape:BoxShape.circle,boxShadow:[BoxShadow(color:Colors.black.withOpacity(0.2),blurRadius:8,offset:constOffset(0,4),),],),child:CircleAvatar(radius:45,backgroundColor:Colors.primaries[index%Colors.primaries.length].withOpacity(0.3),child:constIcon(Icons.person,size:40,color:Colors.white70,),),);}

头像使用Container包裹CircleAvatar,添加阴影效果增加层次感。CircleAvatar显示圆形头像,半径45像素。背景色从primaries颜色列表循环取值,让每个歌手有不同的颜色。

/// 构建歌手名称Widget_buildArtistName(int index){returnColumn(children:[Text('歌手${index+1}',style:constTextStyle(fontSize:14,fontWeight:FontWeight.w500,),maxLines:1,overflow:TextOverflow.ellipsis,),constSizedBox(height:2),Text('${(index+1)*5}首歌曲',style:constTextStyle(fontSize:11,color:Colors.grey,),),],);}}

歌手名称使用14像素字体,fontWeight设为w500稍微加粗。下方显示歌曲数量作为辅助信息,使用灰色小字体。maxLines限制只显示一行,overflow设置溢出时显示省略号。

GridView网格布局详解

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

// 网格布局配置详解GridView.builder(padding:constEdgeInsets.all(16),gridDelegate:constSliverGridDelegateWithFixedCrossAxisCount(crossAxisCount:3,// 每行显示3列childAspectRatio:0.8,// 子项宽高比crossAxisSpacing:16,// 列间距mainAxisSpacing:16,// 行间距),itemCount:30,itemBuilder:(context,index){returnbuildItem(index);},)

padding设置网格的内边距,让内容不会紧贴屏幕边缘。crossAxisCount设为3表示每行显示3个歌手,这个数量在手机屏幕上显示效果较好。

SliverGridDelegateWithFixedCrossAxisCount说明

这个委托类用于定义网格的布局规则:

// 布局参数说明constSliverGridDelegateWithFixedCrossAxisCount(crossAxisCount:3,// 每行显示的列数childAspectRatio:0.8,// 子项的宽高比(宽/高)crossAxisSpacing:16,// 列间距mainAxisSpacing:16,// 行间距)

childAspectRatio设置子项的宽高比,0.8表示高度比宽度大一点,适合显示圆形头像加文字。crossAxisSpacing和mainAxisSpacing分别设置列间距和行间距。

CircleAvatar圆形头像组件

CircleAvatar是Flutter提供的圆形头像组件,非常适合显示用户头像:

// CircleAvatar基本用法CircleAvatar(radius:45,// 头像半径backgroundColor:Colors.blue,// 背景色backgroundImage:NetworkImage(url),// 网络图片child:Icon(Icons.person),// 子组件(无图片时显示))

radius设置头像半径,backgroundColor设置背景色。实际项目中可以使用backgroundImage加载网络图片,child作为图片加载失败时的占位显示。

动态颜色分配

头像使用Colors.primaries数组中的颜色,通过取模运算让每个歌手有不同的颜色:

// 动态颜色分配backgroundColor:Colors.primaries[index%Colors.primaries.length].withOpacity(0.3)

Colors.primaries是Flutter内置的主色调数组,包含18种颜色。取模运算确保index超出数组长度时能循环使用颜色。withOpacity(0.3)降低透明度让颜色更柔和。

阴影效果实现

为头像添加阴影效果增加层次感:

// 阴影效果Container(decoration:BoxDecoration(shape:BoxShape.circle,boxShadow:[BoxShadow(color:Colors.black.withOpacity(0.2),blurRadius:8,offset:constOffset(0,4),),],),child:CircleAvatar(...),)

BoxShadow的color设置阴影颜色,blurRadius设置模糊半径,offset设置阴影偏移。向下偏移4像素让阴影看起来像是光从上方照射。

文本溢出处理

歌手名称可能很长,需要处理溢出情况:

// 文本溢出处理Text('歌手名称',style:constTextStyle(fontSize:14),maxLines:1,overflow:TextOverflow.ellipsis,)

maxLines设为1限制只显示一行,overflow设为TextOverflow.ellipsis在文本溢出时显示省略号。这是处理长文本的常用方式,保证界面整洁。

页面导航实现

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

// 页面导航GestureDetector(onTap:()=>Get.to(()=>ArtistDetailPage(id:index)),child:// 歌手项内容)

Get.to是GetX提供的导航方法,通过构造函数传递歌手ID。详情页可以根据ID加载对应的歌手数据,包括歌手信息、热门歌曲、专辑列表等。

小结

本篇实现了音乐播放器的歌手列表页面。使用GridView.builder实现网格布局,每行显示3个歌手。CircleAvatar用于显示圆形头像,GestureDetector处理点击事件。分类筛选栏让用户可以快速找到感兴趣的歌手类型。这种网格布局在很多App中都会用到,比如通讯录、相册等。通过调整crossAxisCount和childAspectRatio参数,可以轻松适配不同的设计需求。


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

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

AI赋能下编程职业的新角色与职责深入探讨

AI赋能下编程职业的新角色与职责深入探讨 关键词:AI赋能、编程职业、新角色、新职责、技术变革 摘要:本文深入探讨了在AI赋能的背景下,编程职业所出现的新角色与职责。随着AI技术的飞速发展,编程领域发生了巨大的变革,传统的编程角色和职责不断被重塑。文章首先介绍了研究…

作者头像 李华
网站建设 2026/6/10 18:09:15

农作物病虫害检测识别系统|基于YOLOv11+Pytorch + Flask + > SpringBoot|支持玉米、水稻、番茄、草莓病害检测(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定

农作物病虫害检测识别系统|基于YOLOv11Pytorch Flask SpringBoot|支持玉米、水稻、番茄、草莓病害检测(设计源文件万字报告讲解)&#xff08;支持资料、图片参考_相关定制&#xff09;_文章底部可以扫码这是一款基于YOLOv11深度学习模型的农作物病虫害检测识别系统&#xff0…

作者头像 李华
网站建设 2026/6/12 16:34:27

跨境电商,最核心的五大要素是什么?

01 选对适合的平台 做跨境电商一般有两种运营模式&#xff0c;一是独立站交易模式&#xff0c;二是跨境电商平台交易模式&#xff0c;两种模式都不冲突&#xff0c;并且可以互相引流。做独立站是一个企业做大做强必不可少的&#xff0c;如果资金充足可以同时布局。 目前主流的…

作者头像 李华
网站建设 2026/6/13 5:29:50

Python技术应用工程师:互联网行业技能赋能者

在数据驱动的互联网时代&#xff0c;掌握数据分析与人工智能技术已是个人能力的重要评判之一。随着Python技术在数据处理、机器学习等领域的应用&#xff0c;Python技术应用工程师证书成为越来越多人职业发展的选择之一&#xff0c;这个系统化的技能认证&#xff0c;正成为连接…

作者头像 李华
网站建设 2026/6/5 22:02:00

3542. 查找

3542. 查找 ⭐️难度&#xff1a;简单 ⭐️类型&#xff1a;查找 &#x1f4d6;题目&#xff1a;题目链接 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<string.h> #include<vector> // vector不需要.h #include<list> #include<…

作者头像 李华
网站建设 2026/6/13 0:40:05

邦芒干货:想要跳槽成功得拼这6项

想要在职场跳槽中获得成功&#xff0c;需在多个关键维度上进行系统性准备与提升。以下是六个核心要素&#xff0c;它们共同构成了跳槽成功的竞争力基石。‌一、工作能力&#xff1a;核心竞争力的基石‌ 工作能力是职业发展的根本&#xff0c;如同行走江湖所需的“绝世武功”。它…

作者头像 李华