如何在Flutter同一ListView中同时添加文本、音频两类JSON对象数据
Flutter 混合展示文本+音频列表实现方案
1. 封装统一数据结构&合并异步请求
因为需要同时拿到文本、音频两类数据才能做对应展示,首先用Future.wait并行请求两个接口,再把对应数据合并为统一的列表项模型:
// 首先定义列表项统一数据模型 class ListItem { final String contentText; final String audioUrl; // 可根据业务需求扩展章节号、经文序号等字段 ListItem({required this.contentText, required this.audioUrl}); } // 合并请求方法 Future<List<ListItem>> fetchCombinedData() async { // 并行发起两个请求,等待全部返回后再继续处理 final responseList = await Future.wait([ getText(), getAudio(), ]); final TextModel textData = responseList[0] as TextModel; final AudioModel audioData = responseList[1] as AudioModel; List<ListItem> resultList = []; // 按照顺序匹配对应文本和音频:该平台接口返回的经文顺序、数量完全一致,可直接按下标匹配 // 注意:请根据你生成的实际模型层级,调整下面的字段取值路径 for (int surahIndex = 0; surahIndex < textData.data.surahs.length; surahIndex++) { final textSurah = textData.data.surahs[surahIndex]; final audioSurah = audioData.data.surahs[surahIndex]; for (int ayahIndex = 0; ayahIndex < textSurah.ayahs.length; ayahIndex++) { resultList.add(ListItem( contentText: textSurah.ayahs[ayahIndex].text, audioUrl: audioSurah.ayahs[ayahIndex].audio, )); } } return resultList; }
2. 替换原有FutureBuilder,渲染ListView
将原来仅请求文本数据的FutureBuilder替换为监听合并请求的实现,直接渲染每行的文本+音频播放控件:
// 首先在State类中声明合并请求的Future late Future<List<ListItem>> combinedFuture; @override void initState() { super.initState(); // 初始化时触发请求 combinedFuture = fetchCombinedData(); } @override Widget build(BuildContext context) { return FutureBuilder<List<ListItem>>( future: combinedFuture, builder: (context, snapshot) { if (snapshot.hasData) { final itemList = snapshot.data!; return ListView.builder( itemCount: itemList.length, itemBuilder: (context, index) { final currentItem = itemList[index]; return Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Row( children: [ // 展示对应文本 Expanded(child: Text(currentItem.contentText)), // 音频播放按钮,可接入第三方音频库实现播放逻辑 IconButton( icon: const Icon(Icons.play_circle, color: Colors.green), onPressed: () { // 此处实现播放currentItem.audioUrl的逻辑 }, ) ], ), ); }, ); } else if (snapshot.hasError) { return Center(child: Text('加载失败:${snapshot.error}')); } // 加载中展示动画 return const Center(child: CircularProgressIndicator()); }, ); }
优化建议
- 接口返回全量数据量级较大,建议增加分页加载逻辑,避免一次性加载全部数据导致页面卡顿
- 若后续出现数据下标不匹配的情况,可通过经文的唯一编号做匹配,不要直接按下标取值
- 音频播放逻辑可直接使用第三方音频库实现,无需自行封装原生能力
内容的提问来源于stack exchange,提问作者Mohammed Bekele
相关产品推荐
相关产品推荐

