Flutter for OpenHarmony音乐播放器App实战08:搜索实现
搜索功能是音乐播放器中使用频率最高的功能之一。用户可以通过搜索快速找到想听的歌曲、歌手或专辑。本篇文章将详细介绍如何实现一个功能完善的搜索页面包括搜索建议、热门搜索、搜索历史以及多类型搜索结果展示。页面基础结构搜索页面使用StatefulWidget因为需要管理搜索框内容、搜索状态等多个状态变量。importpackage:flutter/material.dart;importpackage:get/get.dart;classSearchPageextendsStatefulWidget{constSearchPage({super.key});overrideStateSearchPagecreateState()_SearchPageState();}页面继承自StatefulWidget使用GetX进行路由管理。搜索页面的交互比较复杂需要响应用户的输入和点击操作。状态变量定义搜索页面需要管理多个状态包括输入控制器、Tab控制器和搜索状态标志。class_SearchPageStateextendsStateSearchPagewithSingleTickerProviderStateMixin{finalTextEditingController_controllerTextEditingController();lateTabController_tabController;bool _showResultfalse;_controller用于控制搜索输入框的内容_tabController用于控制搜索结果的Tab切换_showResult标志决定显示搜索建议还是搜索结果。混入SingleTickerProviderStateMixin是使用TabController的必要条件。生命周期管理在initState中初始化TabController在dispose中释放所有控制器资源。overridevoidinitState(){super.initState();_tabControllerTabController(length:5,vsync:this);}overridevoiddispose(){_controller.dispose();_tabController.dispose();super.dispose();}TabController的length设置为5对应单曲、歌手、专辑、歌单、MV五个搜索类型。dispose方法中同时释放输入控制器和Tab控制器避免内存泄漏。AppBar搜索框设计搜索框直接放在AppBar的title位置这是音乐类App常见的设计模式。overrideWidgetbuild(BuildContextcontext){returnScaffold(appBar:AppBar(title:TextField(controller:_controller,autofocus:true,style:constTextStyle(color:Colors.white),decoration:constInputDecoration(hintText:搜索歌曲、歌手、专辑,hintStyle:TextStyle(color:Colors.white54),border:InputBorder.none,),TextField设置了autofocus为true进入页面时自动弹出键盘。输入文字使用白色提示文字使用半透明白色与深色主题协调。border设置为none让输入框与AppBar融为一体。搜索提交处理用户按下键盘确认键或点击搜索按钮时触发搜索。onSubmitted:(v)setState(()_showResultv.isNotEmpty),),actions:[TextButton(onPressed:()setState(()_showResult_controller.text.isNotEmpty),child:constText(搜索,style:TextStyle(color:Color(0xFFE91E63))),),],),onSubmitted回调在用户按下键盘确认键时触发actions区域放置搜索按钮。两种方式都会检查输入内容是否为空不为空时切换到搜索结果视图。搜索按钮使用主题色突出显示。页面内容切换根据_showResult状态决定显示搜索建议还是搜索结果。body:_showResult?_buildSearchResult():_buildSearchSuggestion(),);}这种条件渲染的方式简洁明了。当用户还没有进行搜索时显示热门搜索和搜索历史搜索后显示搜索结果列表。搜索建议页面搜索建议页面包含热门搜索和搜索历史两个部分。Widget_buildSearchSuggestion(){returnSingleChildScrollView(padding:constEdgeInsets.all(16),child:Column(crossAxisAlignment:CrossAxisAlignment.start,children:[constText(热门搜索,style:TextStyle(fontSize:16,fontWeight:FontWeight.bold),),constSizedBox(height:12),使用SingleChildScrollView包裹整个内容当内容超出屏幕时可以滚动。Column采用左对齐标题使用粗体突出显示。热门搜索标签热门搜索使用Wrap组件实现流式布局的标签展示。Wrap(spacing:8,runSpacing:8,children:List.generate(12,(i)GestureDetector(onTap:(){_controller.text热门${i1};setState(()_showResulttrue);},child:Chip(label:Text(热门${i1}),backgroundColor:constColor(0xFF1E1E1E),),),),),Wrap组件会自动换行spacing和runSpacing分别设置水平和垂直间距。点击标签时会将标签内容填入搜索框并触发搜索。Chip组件使用深色背景与整体主题一致。搜索历史区域搜索历史使用列表形式展示方便用户快速选择之前搜索过的内容。constSizedBox(height:24),constText(搜索历史,style:TextStyle(fontSize:16,fontWeight:FontWeight.bold),),constSizedBox(height:12),ListView.builder(shrinkWrap:true,physics:constNeverScrollableScrollPhysics(),itemCount:5,itemBuilder:(context,i)ListTile(leading:constIcon(Icons.history,color:Colors.grey),title:Text(历史搜索${i1}),trailing:constIcon(Icons.north_west,color:Colors.grey,size:16),),),],),);}ListView.builder设置shrinkWrap为true让列表高度自适应内容。physics设置为NeverScrollableScrollPhysics禁用列表自身的滚动由外层SingleChildScrollView统一处理。每个历史记录前面有时钟图标尾部的箭头图标表示点击可以填入搜索框。搜索结果页面结构搜索结果页面使用TabBar和TabBarView实现多类型结果切换。Widget_buildSearchResult(){returnColumn(children:[TabBar(controller:_tabController,isScrollable:true,labelColor:constColor(0xFFE91E63),unselectedLabelColor:Colors.grey,indicatorColor:constColor(0xFFE91E63),tabs:const[Tab(text:单曲),Tab(text:歌手),Tab(text:专辑),Tab(text:歌单),Tab(text:MV),],),TabBar设置isScrollable为true当Tab数量较多时可以横向滚动。选中的Tab使用主题色未选中使用灰色。五个Tab分别对应不同类型的搜索结果。TabBarView内容区域TabBarView包含五个不同类型的搜索结果列表。Expanded(child:TabBarView(controller:_tabController,children:[_buildSongList(),_buildArtistList(),_buildAlbumList(),_buildPlaylistList(),_buildMVGrid(),],),),],);}使用Expanded让TabBarView占据剩余空间。每个Tab对应一个构建方法分别构建不同类型的结果列表。单曲搜索结果单曲列表展示搜索到的歌曲。Widget_buildSongList(){returnListView.builder(itemCount:20,itemBuilder:(context,i)ListTile(leading:Text(${i1},style:constTextStyle(color:Colors.grey),),title:Text(搜索结果${i1}),subtitle:constText(歌手名),trailing:constIcon(Icons.play_circle_outline,color:Color(0xFFE91E63),),),);}每首歌曲前面显示序号中间显示歌曲名和歌手名尾部是播放按钮。播放按钮使用主题色点击可以直接播放歌曲。歌手搜索结果歌手列表使用圆形头像展示。Widget_buildArtistList(){returnListView.builder(itemCount:10,itemBuilder:(context,i)ListTile(leading:CircleAvatar(backgroundColor:Colors.primaries[i%Colors.primaries.length].withOpacity(0.3),child:constIcon(Icons.person,color:Colors.white70),),title:Text(歌手${i1}),subtitle:Text(${(i1)*100}首歌曲),),);}CircleAvatar用于显示歌手头像背景色根据索引变化。副标题显示歌手的歌曲数量帮助用户了解歌手的作品规模。专辑搜索结果专辑列表使用方形封面展示。Widget_buildAlbumList(){returnListView.builder(itemCount:10,itemBuilder:(context,i)ListTile(leading:Container(width:50,height:50,decoration:BoxDecoration(borderRadius:BorderRadius.circular(8),color:Colors.primaries[i%Colors.primaries.length].withOpacity(0.3),),child:constIcon(Icons.album,color:Colors.white70),),title:Text(专辑${i1}),subtitle:constText(歌手),),);}专辑封面使用圆角矩形与歌手的圆形头像形成区分。Container设置固定宽高保证封面比例一致。歌单搜索结果歌单列表的样式与专辑类似但图标不同。Widget_buildPlaylistList(){returnListView.builder(itemCount:10,itemBuilder:(context,i)ListTile(leading:Container(width:50,height:50,decoration:BoxDecoration(borderRadius:BorderRadius.circular(8),color:Colors.primaries[i%Colors.primaries.length].withOpacity(0.3),),child:constIcon(Icons.queue_music,color:Colors.white70),),title:Text(歌单${i1}),subtitle:Text(${(i1)*50}首),),);}歌单使用queue_music图标副标题显示歌单包含的歌曲数量。这种统一的列表样式让用户容易理解和操作。MV搜索结果MV使用网格布局展示更适合视频类内容。Widget_buildMVGrid(){returnGridView.builder(padding:constEdgeInsets.all(16),gridDelegate:constSliverGridDelegateWithFixedCrossAxisCount(crossAxisCount:2,childAspectRatio:1.5,crossAxisSpacing:12,mainAxisSpacing:12,),itemCount:10,itemBuilder:(context,i)Container(decoration:BoxDecoration(borderRadius:BorderRadius.circular(12),color:Colors.primaries[i%Colors.primaries.length].withOpacity(0.3),),child:constCenter(child:Icon(Icons.play_circle_filled,size:40,color:Colors.white70),),),);}GridView使用SliverGridDelegateWithFixedCrossAxisCount设置每行2个childAspectRatio设置为1.5让MV封面呈现横向矩形。每个MV卡片中间显示播放图标点击可以播放MV。搜索防抖处理为了避免频繁请求接口可以添加搜索防抖功能。Timer?_debounceTimer;void_onSearchChanged(Stringvalue){_debounceTimer?.cancel();_debounceTimerTimer(constDuration(milliseconds:500),(){if(value.isNotEmpty){_performSearch(value);}});}void_performSearch(Stringkeyword){setState(()_showResulttrue);// 调用搜索接口}使用Timer实现防抖用户停止输入500毫秒后才执行搜索。每次输入变化时先取消之前的定时器避免重复请求。清空搜索框搜索框右侧可以添加清空按钮。Widget_buildClearButton(){if(_controller.text.isEmpty)returnconstSizedBox.shrink();returnIconButton(icon:constIcon(Icons.clear,color:Colors.grey),onPressed:(){_controller.clear();setState(()_showResultfalse);},);}只有当搜索框有内容时才显示清空按钮。点击后清空输入内容并返回搜索建议页面。SizedBox.shrink()返回一个零尺寸的Widget不占用任何空间。搜索历史管理搜索历史可以使用SharedPreferences进行本地存储。Futurevoid_saveSearchHistory(Stringkeyword)async{finalprefsawaitSharedPreferences.getInstance();ListStringhistoryprefs.getStringList(search_history)??[];history.remove(keyword);history.insert(0,keyword);if(history.length20){historyhistory.sublist(0,20);}awaitprefs.setStringList(search_history,history);}新的搜索词插入到列表开头如果已存在则先删除再插入保证最新搜索的在最前面。限制历史记录最多20条避免占用过多存储空间。总结搜索功能的实现涉及到多个Flutter组件的综合运用TextField实现搜索输入、Wrap实现流式标签布局、TabBar和TabBarView实现多类型结果切换、ListView和GridView实现不同形式的列表展示。通过合理的状态管理和UI设计为用户提供了流畅的搜索体验。在实际项目中还需要对接后端搜索接口实现真正的搜索功能。欢迎加入开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.net

相关新闻

Nodejs毕设项目推荐-基于nodejs的半亩菜园线上预售系统的设计与实现【附源码+文档,调试定制服务】

Nodejs毕设项目推荐-基于nodejs的半亩菜园线上预售系统的设计与实现【附源码+文档,调试定制服务】

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

2026/7/4 23:02:26 阅读更多 →
Flutter for OpenHarmony音乐播放器App实战14:专辑详情实现

Flutter for OpenHarmony音乐播放器App实战14:专辑详情实现

专辑详情页是展示专辑完整信息的页面,用户可以查看专辑封面、歌手信息、发行时间以及专辑内的所有歌曲。本篇文章将详细介绍如何使用CustomScrollView和Sliver组件实现一个美观实用的专辑详情页面。 页面基础结构 专辑详情页使用StatelessWidget,因为页…

2026/7/3 7:28:33 阅读更多 →
告别贸易商截流!直接和工厂老板谈采购更划算

告别贸易商截流!直接和工厂老板谈采购更划算

在制造业采购工作当中,有一个隐蔽但却普遍存在的痛点长期以来被人们忽视了:你心里以为对接的是“源头厂家”,但实际上却被贸易商一层又一层地截流,他们在价格上加价10%–30%,不承担质量方面的责任,一旦出现…

2026/7/3 15:11:38 阅读更多 →

最新新闻

AI如何助力科研开题报告撰写:选题、文献与格式优化

AI如何助力科研开题报告撰写:选题、文献与格式优化

1. 论文开题报告撰写的痛点与解决方案作为一名经历过无数次开题报告折磨的科研工作者,我深知新手在这个环节面临的种种困境。选题撞车、文献堆砌、逻辑混乱、格式错误......这些问题就像一团乱麻,让许多研究生在学术生涯的起点就举步维艰。记得我第一次写…

2026/7/4 23:02:59 阅读更多 →
抖音下载器终极指南:如何高效批量下载无水印抖音内容

抖音下载器终极指南:如何高效批量下载无水印抖音内容

抖音下载器终极指南:如何高效批量下载无水印抖音内容 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback suppor…

2026/7/4 22:56:56 阅读更多 →
基于VGG-16与PyTorch的人脸识别系统实现

基于VGG-16与PyTorch的人脸识别系统实现

1. 项目概述:基于VGG-16与PyTorch的人脸识别实践 人脸识别作为计算机视觉领域的经典任务,早已从实验室走向日常生活。从手机解锁到门禁系统,这项技术正在改变我们与设备的交互方式。而VGG-16作为卷积神经网络(CNN)的代表性架构,以…

2026/7/4 22:56:56 阅读更多 →
DoWhy因果推断框架:从建模到证伪的四步工程化实践

DoWhy因果推断框架:从建模到证伪的四步工程化实践

1. 项目概述:因果推断不是统计拟合,而是现实世界的“反事实手术”“Causal Inference is a Minefield — Here’s How to Navigate It with DoWhy”这个标题一上来就用了一个非常精准的比喻——矿场。不是“花园”,不是“迷宫”,更…

2026/7/4 22:56:55 阅读更多 →
ChatGPT插件API密钥安全管理实战:从架构设计到自动化轮换

ChatGPT插件API密钥安全管理实战:从架构设计到自动化轮换

1. 项目概述:为什么ChatGPT插件密钥安全是生死线最近在折腾各种AI工具和插件,发现一个挺普遍但又被很多人忽视的问题:ChatGPT插件的API密钥管理。无论是自己开发插件,还是使用别人的,密钥泄露的风险都像悬在头顶的达摩…

2026/7/4 22:52:53 阅读更多 →
基于YOLOv8-seg的高精度道路缺陷检测系统开发

基于YOLOv8-seg的高精度道路缺陷检测系统开发

1. 项目背景与核心价值道路缺陷检测是智慧交通和市政养护领域的关键技术痛点。传统人工巡检方式存在效率低、漏检率高、主观性强等问题,尤其在夜间或恶劣天气条件下表现更差。我们团队基于YOLOv8-seg框架,融合EfficientRepBiPAN、AFPN-P345等50余项创新改…

2026/7/4 22:50:52 阅读更多 →

日新闻

Memcached 1.6.43 发布:关键安全修复版本,多项问题得到解决

Memcached 1.6.43 发布:关键安全修复版本,多项问题得到解决

Memcached 1.6.43 正式发布,这是一个关键的安全修复版本,修复了多个方面的问题,还对部分功能进行了优化。 安全修复亮点 此次发布在安全修复上表现突出。binprot 避免了项目引用计数溢出,mcmc 因安全问题提升了上游版本号&#xf…

2026/7/4 0:04:29 阅读更多 →
终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案

终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案

终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案 【免费下载链接】HMCL A Minecraft Launcher which is multi-functional, cross-platform and popular 项目地址: https://gitcode.com/gh_mirrors/hm/HMCL HMCL(Hello Minecraft! Lau…

2026/7/4 0:06:29 阅读更多 →
KMX63与PIC18F66K40在嵌入式HMI中的硬件协同与低功耗设计

KMX63与PIC18F66K40在嵌入式HMI中的硬件协同与低功耗设计

1. KMX63与PIC18F66K40的硬件协同架构解析KMX63作为一款三轴加速度计和磁力计组合传感器,与PIC18F66K40微控制器的搭配堪称嵌入式HMI开发的黄金组合。这套硬件组合的核心优势在于KMX63提供的高精度运动感知能力与PIC18F66K40强大的信号处理能力形成了完美互补。KMX6…

2026/7/4 0:06:29 阅读更多 →

周新闻

月新闻