Flutter:如何在CustomSearchDelegate中访问Song类的songTitle变量?
解决Flutter搜索栏无法访问Song类songTitle的问题
你遇到的核心问题是将List<Widget>传入搜索代理后,编译器仅能识别元素为通用Widget类型,而Widget类并没有songTitle属性。你把歌曲数据和UI组件绑定在一起,导致搜索逻辑无法直接访问数据字段。
解决方案:分离数据模型与UI组件
正确的做法是将歌曲数据与展示它的Widget解耦,让搜索逻辑只处理纯数据,UI组件负责渲染。
1. 创建独立的歌曲数据模型
定义一个纯数据类存储歌曲信息,与UI完全分离:
class SongData { final String songTitle; const SongData({required this.songTitle}); }
2. 修改Recordings中的歌曲列表
将原来的List<Widget>替换为List<SongData>,存储纯数据:
class Recordings extends StatelessWidget { Recordings({Key? key, required this.title}) : super(key: key); final String title; // 存储纯数据而非Widget final List<SongData> songs = [ const SongData(songTitle: "September"), const SongData(songTitle: "Don't Stop Me Now"), const SongData(songTitle: "Let It Go"), const SongData(songTitle: "Smoke on the Water"), const SongData(songTitle: "Don't Kill My Vibe"), const SongData(songTitle: "Mamma Mia"), const SongData(songTitle: "4'33"), ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(title), actions: [ IconButton( onPressed: () { // 传入数据列表而非Widget列表 showSearch(context: context, delegate: CustomSearchDelegate(searchTerms: songs)); }, icon: const Icon(Icons.search)), ], ), body: Container( color: Colors.grey[600], child: Center( child: ListView( // 将数据转换为对应的UI组件 children: songs.map((songData) => Song(songData: songData)).toList(), ), ), ), ); } }
3. 更新Song Widget以接收数据模型
让Song Widget接收SongData对象,从数据中提取标题:
class Song extends StatelessWidget { const Song({Key? key, required this.songData}) : super(key: key); final SongData songData; @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(10), child: Container( alignment: Alignment.center, color: Colors.lightBlue[300], child: Text( songData.songTitle, style: const TextStyle(color: Colors.black), ), ), ); } }
4. 修改CustomSearchDelegate处理数据模型
现在搜索代理接收List<SongData>,可直接访问songTitle属性:
class CustomSearchDelegate extends SearchDelegate { CustomSearchDelegate({required this.searchTerms}); final List<SongData> searchTerms; @override List<Widget> buildActions(BuildContext context) { return [ IconButton( onPressed: () { query = ""; }, icon: const Icon(Icons.clear), ), ]; } @override Widget buildLeading(BuildContext context) { return IconButton( onPressed: () { close(context, null); }, icon: const Icon(Icons.arrow_back), ); } @override Widget buildResults(BuildContext context) { List<SongData> matchQuery = []; for (var song in searchTerms) { // 直接访问songTitle进行搜索匹配 if (song.songTitle.toLowerCase().contains(query.toLowerCase())) { matchQuery.add(song); } } return ListView.builder( itemCount: matchQuery.length, itemBuilder: ((context, index) { var result = matchQuery[index]; // 使用Song Widget渲染搜索结果 return Song(songData: result); }), ); } @override Widget buildSuggestions(BuildContext context) { List<SongData> matchQuery = []; for (var song in searchTerms) { if (song.songTitle.toLowerCase().contains(query.toLowerCase())) { matchQuery.add(song); } } return ListView.builder( itemCount: matchQuery.length, itemBuilder: ((context, index) { var result = matchQuery[index]; return Song(songData: result); }), ); } }
额外优化建议
- 若后续从数据库获取数据,可直接扩展
SongData添加歌手名、时长等属性,无需修改UI组件 - 提取重复的搜索匹配逻辑为单独方法,减少代码冗余:
List<SongData> _getMatchingSongs() { return searchTerms.where((song) => song.songTitle.toLowerCase().contains(query.toLowerCase()) ).toList(); }
之后在buildResults和buildSuggestions中直接调用该方法即可。
内容的提问来源于stack exchange,提问作者C Hale
相关产品推荐
相关产品推荐

