news 2026/4/30 9:27:26

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Flutter for OpenHarmony音乐播放器App实战11:创建歌单实现

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

页面基础结构

创建歌单页面使用StatefulWidget,因为需要管理输入框内容和开关状态。

import'package:flutter/material.dart';import'package:get/get.dart';classCreatePlaylistPageextendsStatefulWidget{constCreatePlaylistPage({super.key});@overrideState<CreatePlaylistPage>createState()=>_CreatePlaylistPageState();}

页面继承自StatefulWidget,使用GetX进行路由管理。创建歌单的交互相对简单,但需要响应用户的输入和开关操作。

状态变量定义

页面需要管理输入控制器和隐私开关状态。

class_CreatePlaylistPageStateextendsState<CreatePlaylistPage>{final_nameController=TextEditingController();bool _isPrivate=false;@overridevoiddispose(){_nameController.dispose();super.dispose();}

_nameController用于控制歌单名称输入框,_isPrivate标识歌单是否设为私密。在dispose方法中释放控制器资源,这是Flutter开发中的标准做法。

AppBar设计

AppBar包含标题和完成按钮。

@overrideWidgetbuild(BuildContextcontext){returnScaffold(appBar:AppBar(title:constText('创建歌单'),actions:[TextButton(onPressed:()=>_createPlaylist(),child:constText('完成',style:TextStyle(color:Color(0xFFE91E63)),),),],),

完成按钮使用主题色,放在AppBar右侧。点击后调用_createPlaylist方法提交创建请求。这种设计符合用户的操作习惯。

页面主体布局

页面主体使用Padding包裹Column,垂直排列各个组件。

body:Padding(padding:constEdgeInsets.all(16),child:Column(crossAxisAlignment:CrossAxisAlignment.start,children:[_buildCoverPicker(),constSizedBox(height:24),_buildNameInput(),constSizedBox(height:16),_buildPrivacySwitch(),],),),);}

页面包含三个主要部分:封面选择、名称输入和隐私设置。使用SizedBox控制各部分之间的间距。

封面选择组件

封面选择区域居中显示,点击可以选择图片。

Widget_buildCoverPicker(){returnCenter(child:GestureDetector(onTap:()=>_pickCoverImage(),child:Container(width:120,height:120,decoration:BoxDecoration(borderRadius:BorderRadius.circular(12),color:constColor(0xFF1E1E1E),),child:constColumn(mainAxisAlignment:MainAxisAlignment.center,children:[Icon(Icons.add_photo_alternate,size:40,color:Colors.grey),SizedBox(height:8),Text('添加封面',style:TextStyle(color:Colors.grey,fontSize:12)),],),),),);}

封面容器使用深色背景,圆角设置为12。内部垂直排列图标和文字,提示用户点击添加封面。GestureDetector包裹整个容器,扩大点击区域。

选择封面图片

点击封面区域后调用图片选择方法。

void_pickCoverImage()async{// 实际项目中使用image_picker插件Get.bottomSheet(Container(decoration:constBoxDecoration(color:Color(0xFF1E1E1E),borderRadius:BorderRadius.vertical(top:Radius.circular(16)),),child:Column(mainAxisSize:MainAxisSize.min,children:[ListTile(leading:constIcon(Icons.camera_alt),title:constText('拍照'),onTap:(){Get.back();Get.snackbar('提示','相机功能开发中');},),ListTile(leading:constIcon(Icons.photo_library),title:constText('从相册选择'),onTap:(){Get.back();Get.snackbar('提示','相册功能开发中');},),constSizedBox(height:16),],),),);}

使用底部菜单提供拍照和从相册选择两个选项。实际项目中需要使用image_picker插件来实现真正的图片选择功能。

名称输入组件

歌单名称输入区域包含标签和输入框。

Widget_buildNameInput(){returnColumn(crossAxisAlignment:CrossAxisAlignment.start,children:[constText('歌单名称',style:TextStyle(fontWeight:FontWeight.bold),),constSizedBox(height:8),TextField(controller:_nameController,decoration:InputDecoration(hintText:'请输入歌单名称',filled:true,fillColor:constColor(0xFF1E1E1E),border:OutlineInputBorder(borderRadius:BorderRadius.circular(12),borderSide:BorderSide.none,),),),],);}

输入框使用深色填充背景,无边框设计。hintText提示用户输入内容。圆角与封面容器保持一致,视觉上更加协调。

隐私设置开关

使用SwitchListTile实现隐私设置。

Widget_buildPrivacySwitch(){returnSwitchListTile(title:constText('设为私密'),subtitle:constText('私密歌单仅自己可见'),value:_isPrivate,onChanged:(v)=>setState(()=>_isPrivate=v),activeColor:constColor(0xFFE91E63),contentPadding:EdgeInsets.zero,);}

SwitchListTile集成了标题、副标题和开关,非常适合这种设置项。activeColor设置为主题色,contentPadding设为零与其他组件对齐。

创建歌单方法

点击完成按钮后执行创建逻辑。

void_createPlaylist(){finalname=_nameController.text.trim();if(name.isEmpty){Get.snackbar('提示','请输入歌单名称',snackPosition:SnackPosition.BOTTOM,backgroundColor:Colors.red.withOpacity(0.8),colorText:Colors.white,);return;}// 模拟创建歌单Get.back(result:{'name':name,'isPrivate':_isPrivate,});Get.snackbar('成功','歌单创建成功',snackPosition:SnackPosition.BOTTOM,backgroundColor:Colors.green.withOpacity(0.8),colorText:Colors.white,);}

首先验证歌单名称是否为空,为空时显示错误提示。创建成功后返回上一页并传递创建结果,同时显示成功提示。

输入验证增强

可以添加更多的输入验证规则。

bool_validateInput(){finalname=_nameController.text.trim();if(name.isEmpty){_showError('请输入歌单名称');returnfalse;}if(name.length>40){_showError('歌单名称不能超过40个字符');returnfalse;}// 检查是否包含特殊字符finalregex=RegExp(r'[<>"\\/|?*]');if(regex.hasMatch(name)){_showError('歌单名称不能包含特殊字符');returnfalse;}returntrue;}void_showError(Stringmessage){Get.snackbar('提示',message,snackPosition:SnackPosition.BOTTOM,backgroundColor:Colors.red.withOpacity(0.8),colorText:Colors.white,);}

验证规则包括非空检查、长度限制和特殊字符检查。将错误提示抽取为独立方法,避免代码重复。

歌单描述输入

可以添加歌单描述输入框。

Widget_buildDescriptionInput(){returnColumn(crossAxisAlignment:CrossAxisAlignment.start,children:[constText('歌单简介',style:TextStyle(fontWeight:FontWeight.bold),),constSizedBox(height:8),TextField(controller:_descController,maxLines:4,maxLength:200,decoration:InputDecoration(hintText:'介绍一下这个歌单吧(选填)',filled:true,fillColor:constColor(0xFF1E1E1E),border:OutlineInputBorder(borderRadius:BorderRadius.circular(12),borderSide:BorderSide.none,),),),],);}

描述输入框设置maxLines为4,允许多行输入。maxLength限制最大字符数,输入框会自动显示字符计数。

标签选择功能

可以为歌单添加标签。

finalList<String>_availableTags=['流行','摇滚','民谣','电子','古典','爵士','R&B','说唱'];finalSet<String>_selectedTags={};Widget_buildTagSelector(){returnColumn(crossAxisAlignment:CrossAxisAlignment.start,children:[constText('歌单标签',style:TextStyle(fontWeight:FontWeight.bold),),constSizedBox(height:8),Wrap(spacing:8,runSpacing:8,children:_availableTags.map((tag){finalisSelected=_selectedTags.contains(tag);returnGestureDetector(onTap:(){setState((){if(isSelected){_selectedTags.remove(tag);}elseif(_selectedTags.length<3){_selectedTags.add(tag);}else{Get.snackbar('提示','最多选择3个标签');}});},child:Chip(label:Text(tag),backgroundColor:isSelected?constColor(0xFFE91E63):constColor(0xFF1E1E1E),labelStyle:TextStyle(color:isSelected?Colors.white:Colors.grey,),),);}).toList(),),],);}

使用Wrap组件实现流式布局,标签会自动换行。选中的标签使用主题色背景,限制最多选择3个标签。

封面预览

选择封面后显示预览。

String?_coverPath;Widget_buildCoverPreview(){returnCenter(child:GestureDetector(onTap:()=>_pickCoverImage(),child:Container(width:120,height:120,decoration:BoxDecoration(borderRadius:BorderRadius.circular(12),color:constColor(0xFF1E1E1E),image:_coverPath!=null?DecorationImage(image:FileImage(File(_coverPath!)),fit:BoxFit.cover,):null,),child:_coverPath==null?constColumn(mainAxisAlignment:MainAxisAlignment.center,children:[Icon(Icons.add_photo_alternate,size:40,color:Colors.grey),SizedBox(height:8),Text('添加封面',style:TextStyle(color:Colors.grey,fontSize:12)),],):Stack(children:[Positioned(right:4,top:4,child:Container(padding:constEdgeInsets.all(4),decoration:constBoxDecoration(color:Colors.black54,shape:BoxShape.circle,),child:constIcon(Icons.edit,size:16,color:Colors.white),),),],),),),);}

如果已选择封面,使用DecorationImage显示图片,右上角显示编辑图标。未选择时显示添加提示。

加载状态处理

创建歌单时显示加载状态。

bool _isLoading=false;void_createPlaylist()async{if(!_validateInput())return;setState(()=>_isLoading=true);try{// 模拟网络请求awaitFuture.delayed(constDuration(seconds:1));Get.back(result:{'name':_nameController.text.trim(),'isPrivate':_isPrivate,'tags':_selectedTags.toList(),});Get.snackbar('成功','歌单创建成功');}catch(e){Get.snackbar('错误','创建失败,请重试');}finally{if(mounted){setState(()=>_isLoading=false);}}}

创建过程中设置_isLoading为true,完成后恢复。使用try-catch处理可能的错误,finally确保状态恢复。

完成按钮状态

根据加载状态和输入内容控制按钮状态。

Widget_buildSubmitButton(){returnTextButton(onPressed:_isLoading||_nameController.text.trim().isEmpty?null:()=>_createPlaylist(),child:_isLoading?constSizedBox(width:20,height:20,child:CircularProgressIndicator(strokeWidth:2,valueColor:AlwaysStoppedAnimation<Color>(Color(0xFFE91E63)),),):constText('完成',style:TextStyle(color:Color(0xFFE91E63)),),);}

加载中显示CircularProgressIndicator,名称为空时按钮禁用。这种设计提供了清晰的状态反馈。

总结

创建歌单页面虽然功能相对简单,但涉及到表单输入、状态管理、输入验证等多个Flutter开发中的常见场景。通过合理的组件拆分和状态管理,让代码结构清晰、易于维护。在实际项目中,还需要对接后端接口实现真正的歌单创建功能,以及使用image_picker等插件实现图片选择。

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

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

收藏备用|大模型6大核心职业方向拆解,小白程序员也能快速入局

当下&#xff0c;大模型技术正以不可阻挡之势渗透各行各业&#xff0c;从日常智能交互到产业深度升级&#xff0c;其应用身影无处不在&#xff0c;更给程序员群体开辟了一条全新的职业上升赛道&#xff0c;成为转型加薪的核心突破口。本文专为想要转型大模型领域的小白程序员、…

作者头像 李华
网站建设 2026/5/1 4:43:03

【课程设计/毕业设计】基于.Net智慧阅读书城系统的设计与实现基于net的网上书城系统线上阳光好书系统【附源码、数据库、万字文档】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/5/1 4:42:27

架构评审与技术债治理——质量属性、演进式重构与风险评估框架

**写在前面&#xff0c;本人目前处于求职中&#xff0c;如有合适内推岗位&#xff0c;请加&#xff1a;lpshiyue 感谢。 优秀的架构不是一次性的设计杰作&#xff0c;而是通过持续评审、债务治理和渐进式重构形成的有机体系 在构建了高可用的容灾体系后&#xff0c;我们面临一个…

作者头像 李华
网站建设 2026/5/1 6:54:34

Java开发者必看!从入门到精通掌握AI应用开发(建议收藏)

LangChat Team推出面向Java开发者的AI应用开发完整学习路径&#xff0c;包含40篇从入门到精通的教程及配套可运行代码。基于LangChain4j 1.10.0、Java 17和Spring Boot 3.2构建&#xff0c;覆盖RAG、Agent、多模态等前沿技术。项目分7个阶段系统学习&#xff0c;从基础概念到企…

作者头像 李华