Flutter中FutureBuilder嵌套GroupedListView出现类型转换错误求助
在Flutter应用中使用GroupedListView按指定字段分组显示书签时,静态列表显示正常,但通过FutureBuilder从SharedPreferences加载动态数据时,出现错误:type 'string' is not a subtype of type 'int' of 'index'。
相关代码
class _BookmarkScreenState extends State<BookmarkScreen> { List bookmarks =[]; @override void initState() { super.initState(); _loadData(); setState(() {}); } Future<List<String>> _loadData() async { SharedPreferences pref = await SharedPreferences.getInstance(); List<String> lb = await pref.getStringList(('bookmarks_key')); print(lb); lb.forEach((element) { bookmarks.add(element); }); print(bookmarks); return bookmarks; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Bookmark Screen")), body: Container( child: FutureBuilder( future: _loadData(), builder: (context, AsyncSnapshot snapshot) { if (!snapshot.hasData) { return Center(child: CircularProgressIndicator()); } else { return GroupedListView<dynamic, String>( // 报错行 elements: bookmarks, groupBy: (element) => element['surah_name'], groupComparator: (value1, value2) => value2.compareTo(value1), itemComparator: (item1, item2) => item1['verse_no'].compareTo(item2['verse_no']), order: GroupedListOrder.DESC, useStickyGroupSeparators: true, groupSeparatorBuilder: (String value) => Padding( padding: const EdgeInsets.all(8.0), child: Text( value, textAlign: TextAlign.center, style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), )), itemBuilder: ((c, element) { return Card( elevation: 8.0, margin: new EdgeInsets.symmetric( horizontal: 10.0, vertical: 6.0), child: Container( child: ListTile( contentPadding: EdgeInsets.symmetric( horizontal: 20.0, vertical: 10.0), title: Text(element['verse_no'].toString()), ), ), ); }));}}))); } }
静态正常示例
List bookmarks = [{"surah_no":2,"verse_no":2,"surah_name":"Al-Baqarah","favorite":true}, {"surah_no":2,"verse_no":1,"surah_name":"Al-Baqarah","favorite":true}, {"surah_no":2,"verse_no":2,"surah_name":"Al-Baqarah","favorite":true}, {"surah_no":2,"verse_no":3,"surah_name":"Al-Baqarah","favorite":true}, {"surah_no":4,"verse_no":1,"surah_name":"An-Nisa'","favorite":true}, {"surah_no":8,"verse_no":1,"surah_name":"Al-Anfal","favorite":true},];
问题原因
- 数据类型不匹配:SharedPreferences的
getStringList返回List<String>,你直接将字符串元素添加到bookmarks列表,导致列表元素是字符串而非静态列表中的Map对象。当GroupedListView尝试用element['surah_name']访问时,会把字符串当作数组,用字符串'surah_name'作为索引(要求是int类型),因此抛出类型转换错误。 - 无效状态更新:
initState中调用异步的_loadData后立即执行setState,此时数据尚未加载完成,属于无效操作。 - 重复加载数据:
build方法每次执行都会调用_loadData(),导致SharedPreferences被重复读取,可能引发数据混乱。
修复步骤
1. 序列化/反序列化数据
存储书签时需将Map转为JSON字符串,读取时再解析回Map。修改_loadData方法:
Future<List<Map<String, dynamic>>> _loadData() async { SharedPreferences pref = await SharedPreferences.getInstance(); List<String>? lb = pref.getStringList('bookmarks_key'); if (lb == null) return []; // 将每个JSON字符串解析为Map List<Map<String, dynamic>> loadedBookmarks = lb.map((jsonStr) { return jsonDecode(jsonStr) as Map<String, dynamic>; }).toList(); setState(() { bookmarks = loadedBookmarks; }); return loadedBookmarks; }
同时确保存储书签时的处理逻辑(示例存储代码):
Future<void> saveBookmark(Map<String, dynamic> bookmark) async { SharedPreferences pref = await SharedPreferences.getInstance(); List<String> existing = pref.getStringList('bookmarks_key') ?? []; existing.add(jsonEncode(bookmark)); await pref.setStringList('bookmarks_key', existing); }
2. 修正FutureBuilder与状态管理
- 调整
_loadData返回类型为List<Map<String, dynamic>>,匹配实际数据类型。 - 直接使用FutureBuilder的
snapshot.data作为GroupedListView的数据源,避免依赖全局列表:
// 修改FutureBuilder部分 FutureBuilder<List<Map<String, dynamic>>>( future: _loadData(), builder: (context, AsyncSnapshot<List<Map<String, dynamic>>> snapshot) { if (!snapshot.hasData) { return const Center(child: CircularProgressIndicator()); } final bookmarks = snapshot.data!; return GroupedListView<Map<String, dynamic>, String>( elements: bookmarks, groupBy: (element) => element['surah_name'] as String, groupComparator: (value1, value2) => value2.compareTo(value1), itemComparator: (item1, item2) => (item1['verse_no'] as int).compareTo(item2['verse_no'] as int), order: GroupedListOrder.DESC, useStickyGroupSeparators: true, groupSeparatorBuilder: (String value) => Padding( padding: const EdgeInsets.all(8.0), child: Text( value, textAlign: TextAlign.center, style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), ), itemBuilder: ((c, element) { return Card( elevation: 8.0, margin: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0), child: ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0), title: Text(element['verse_no'].toString()), ), ); }), ); }, )
3. 优化initState
移除initState中多余的setState:
@override void initState() { super.initState(); _loadData(); }
内容的提问来源于stack exchange,提问作者Noor
相关产品推荐
相关产品推荐

