如何从SQFLITE创建的List中正确获取单个字段值?
问题说明
开发基于Flutter的本地便签应用,使用sqflite做数据持久化,支持用户输入标题(title)、描述(description)保存,需求为点击保存后,在Card组件构成的列表中支持三种展示规则:仅展示标题、仅展示描述、同时展示标题与描述。
在buildNotes方法中编写child: Text(notes[NoteFields.description])读取描述内容时,无法正常获取数据,抛出如下错误:
type 'String' is not a subtype of type 'int' of 'index'
原实现代码如下:
class NotesPage extends StatefulWidget { @override _NotesPageState createState() => _NotesPageState(); } class _NotesPageState extends State<NotesPage> { late List<Note> notes; bool isLoading = false; @override void initState() { super.initState(); refreshNotes(); } @override void dispose() { NotesDatabase.instance.close(); super.dispose(); } Future refreshNotes() async { setState(() => isLoading = true); this.notes = await NotesDatabase.instance.readAllNotes(); setState(() => isLoading = false); } @override Widget build(BuildContext context) => Scaffold( appBar: AppBar( title: Text('Notes', style: TextStyle(fontSize: 24)), actions: [Icon(Icons.search), SizedBox(width: 12)], ), body: Center( child: isLoading ? CircularProgressIndicator() : notes.isEmpty ? Text('No Notes', style: TextStyle(color: Colors.white, fontSize: 24)) : buildNotes(notes), ), floatingActionButton: FloatingActionButton( backgroundColor: Colors.black, child: Icon(Icons.add), onPressed: () async { await Navigator.of(context).push( MaterialPageRoute(builder: (context) => AddEditNotePage()), ); refreshNotes(); }, ), ); Widget buildNotes(notes) => Card( child: Text(notes[NoteFields.description]), ); }
错误原因
- 传入
buildNotes的notes是List<Note>类型的对象数组,List仅支持int类型的下标作为索引获取对应位置的元素,代码中传入NoteFields.description(String类型的字段名)作为索引,直接触发类型不匹配错误。 - 代码混淆了List和Map的取值逻辑:如果是从sqflite直接查出的
List<Map<String, dynamic>>结构,可以用字符串键取字段值,但查询结果已经被转为List<Note>实体类列表,不能再用Map的键值语法取值。 - 现有
buildNotes仅返回单个Card组件,没有遍历整个便签列表,无法渲染多条数据。
修复方案
- 先确认Note实体类的序列化/反序列化逻辑正常,保证从数据库查出的Map数据能正确转为Note对象:
// 数据库字段常量类 class NoteFields { static const String id = '_id'; static const String title = 'title'; static const String description = 'description'; static final List<String> values = [id, title, description]; } // Note实体类 class Note { final int? id; final String title; final String description; Note({ this.id, required this.title, required this.description, }); // 数据库查询结果Map转Note对象 factory Note.fromMap(Map<String, dynamic> map) { return Note( id: map[NoteFields.id] as int?, title: map[NoteFields.title] as String, description: map[NoteFields.description] as String, ); } }
- 重写
buildNotes方法,用ListView.builder遍历列表渲染所有便签,通过int下标取到单个Note对象后,直接访问对象属性取值,同时增加展示模式控制逻辑,支持三种展示规则:
// 展示模式枚举 enum NoteDisplayMode { onlyTitle, onlyDescription, showAll, } // 在_NotesPageState中增加当前展示模式的状态变量 NoteDisplayMode displayMode = NoteDisplayMode.showAll; Widget buildNotes(List<Note> notes) { return ListView.builder( padding: EdgeInsets.all(12), itemCount: notes.length, itemBuilder: (context, index) { // 用int下标取当前位置的单个Note对象 final Note currentNote = notes[index]; Widget cardContent; // 根据当前展示模式拼接内容 switch (displayMode) { case NoteDisplayMode.onlyTitle: cardContent = Text( currentNote.title, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500), ); break; case NoteDisplayMode.onlyDescription: cardContent = Text(currentNote.description); break; case NoteDisplayMode.showAll: cardContent = Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( currentNote.title, style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), SizedBox(height: 6), Text(currentNote.description), ], ); break; } return Card( margin: EdgeInsets.symmetric(vertical: 6), child: Padding( padding: EdgeInsets.all(12), child: cardContent, ), ); }, ); }
- 如果需要切换展示模式,只需修改
displayMode状态变量并触发setState即可。
内容的提问来源于stack exchange,提问作者Ashish Magar
相关产品推荐
相关产品推荐

