Flutter应用启动时SQL数据库数据无法在UI中显示问题排查
Flutter应用重启后UI无法显示数据库数据的问题
添加若干数据后关闭应用,再次重启时UI显示为空,但实际数据库并非空的。已使用FutureBuilder组件,且在启动时调用refreshAllNames方法,恳请帮忙排查代码中的问题。
我的代码
import 'package:flutter/material.dart'; import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; void main() { runApp(MyApp()); } class MyApp extends StatefulWidget { @override MyAppState createState() => MyAppState(); } class DataFields { static final List<String> values = [id, name]; static final String id = '_id'; static final String name = 'myName'; } class DataModel { int id; String name; DataModel({required this.id, required this.name}); DataModel copy( {int? id, String? title, String? description, int? isSelected}) => DataModel(id: id ?? this.id, name: name ?? this.name); Map<String, Object?> toJson() => {if (id != null) DataFields.id: id, DataFields.name: name}; static DataModel fromJson(Map<String, Object?> json) => DataModel( id: json[DataFields.id] as int, name: json[DataFields.name] as String); } class DBHelper { final String myTable = 'myDataTable'; static const String myDatabase = 'myDB.db'; DBHelper._init(); static final DBHelper instance = DBHelper._init(); static Database? db; Future<Database> get dataBase async { if (db != null) { return db!; } else { db = await initDB(myDatabase); return db!; } } Future<Database> initDB(String filePath) async { final dbPath = await getDatabasesPath(); final path = join(dbPath, filePath); return await openDatabase(path, version: 1, onCreate: createDB); } Future createDB(Database db, int version) async { var idType = 'INTEGER PRIMARY KEY AUTOINCREMENT'; var textType = 'TEXT NOT NULL'; await db.execute('''CREATE TABLE $myTable (${DataFields.id} $idType,${DataFields.name} $textType)'''); } Future<DataModel> create(DataModel datamodel) async { final db = await instance.dataBase; final id = await db.insert(myTable, datamodel.toJson()); return datamodel.copy(id: id); } Future<DataModel> readData(int id) async { final db = await instance.dataBase; final maps = await db.query(myTable, columns: DataFields.values, where: '${DataFields.id} = ?', whereArgs: [id]); if (maps.isNotEmpty) { return DataModel.fromJson(maps.first); } else { throw Exception('ID $id not found'); } } Future<List<DataModel>> readAllNames() async { final db = await instance.dataBase; final result = await db.rawQuery('SELECT * FROM $myTable'); final list = result.map((json) => DataModel.fromJson(json)).toList(); if (list.isNotEmpty) return list; else throw Exception('Empty List'); } Future<int> countAllDataRows() async { final db = await instance.dataBase; var x = await db.rawQuery('SELECT COUNT(1) FROM $myTable'); return x.length; } Future<int> update(DataModel datamodel) async { final db = await instance.dataBase; return db.update(myTable, datamodel.toJson(), where: '${DataFields.id} = ?', whereArgs: [datamodel.id]); } Future<int> delete(int id) async { final db = await instance.dataBase; return await db .delete(myTable, where: '${DataFields.id} = ?', whereArgs: [id]); } Future close() async { final db = await instance.dataBase; db.close(); } } class MyAppState extends State<MyApp> { TextEditingController mc = TextEditingController(); static late List<DataModel> mylist = <DataModel>[]; int index = 0; int myindex = 0; bool isLoading = false; @override void initState() { super.initState(); refreshNames(); } @override void dispose() { mc.dispose(); DBHelper.instance.close(); super.dispose(); } Future refreshNames() async { setState(() => isLoading = true); mylist = await DBHelper.instance.readAllNames(); setState(() => isLoading = false); } addItem() { setState(() { if (mc.text.isNotEmpty) { index++; mylist.add(DataModel(id: index, name: mc.text)); DBHelper.instance.create(DataModel(id: index, name: mc.text)); refreshNames(); } }); } removeItem() { setState(() { if (index > 0) { index; mylist.removeAt(index); DBHelper.instance.delete(index); refreshNames(); } }); } @override Widget build(BuildContext context) { Widget space = SizedBox(width: 100, height: 20, child: Text("")); Widget myinput = TextField( controller: mc, decoration: InputDecoration(border: OutlineInputBorder(), labelText: 'Name')); Widget btnAdd = ElevatedButton(onPressed: addItem, child: Text('Add')); Widget btnRemove = ElevatedButton(onPressed: removeItem, child: Text('Remove')); Widget newList = SizedBox( width: 800, height: 500, child: FutureBuilder<List<DataModel>?>(builder: (BuildContext context, AsyncSnapshot<List<DataModel>?> snapshot) { return ListView.separated( padding: const EdgeInsets.all(8), itemCount: mylist.length, itemBuilder: (BuildContext context, int index) { return Container( height: 100, child: Center(child: Text('${mylist[index].name}')), ); }, separatorBuilder: (BuildContext context, int index) => const Divider(), ); })); return MaterialApp( home: Scaffold( resizeToAvoidBottomInset: false, body: SingleChildScrollView(scrollDirection: Axis.vertical, child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisSize: MainAxisSize.min, children: <Widget>[ Container( padding: const EdgeInsets.all(5), child: SingleChildScrollView( child: Column( mainAxisAlignment:MainAxisAlignment.spaceEvenly, children: <Widget>[ space,space,myinput,space,btnAdd,space,btnRemove,space,newList,space ]))) ])))); } }
问题排查与修复方案
1. FutureBuilder未正确使用
当前FutureBuilder完全未利用自身的snapshot数据,而是直接依赖全局mylist,违背组件设计逻辑。需绑定异步任务并根据snapshot状态处理UI:
Widget newList = SizedBox( width: 800, height: 500, child: FutureBuilder<List<DataModel>>( future: DBHelper.instance.readAllNames(), builder: (BuildContext context, AsyncSnapshot<List<DataModel>> snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { return Center(child: Text('加载失败: ${snapshot.error}')); } else if (!snapshot.hasData || snapshot.data!.isEmpty) { return Center(child: Text('暂无数据')); } else { final mylist = snapshot.data!; return ListView.separated( padding: const EdgeInsets.all(8), itemCount: mylist.length, itemBuilder: (BuildContext context, int index) { return Container( height: 100, child: Center(child: Text('${mylist[index].name}')), ); }, separatorBuilder: (BuildContext context, int index) => const Divider(), ); } }, ), );
2. readAllNames方法抛出异常导致加载失败
数据库为空时该方法抛出异常,会直接中断数据加载流程,应改为返回空列表:
Future<List<DataModel>> readAllNames() async { final db = await instance.dataBase; final result = await db.rawQuery('SELECT * FROM $myTable'); return result.map((json) => DataModel.fromJson(json)).toList(); }
3. 自定义index与数据库自增ID冲突
手动维护的index和数据库自动生成的ID不一致,导致重启后数据匹配错误。插入时不要传入ID,让数据库自动生成:
// 修改DataModel的toJson方法 Map<String, Object?> toJson() => { DataFields.name: name }; // 修改addItem方法 addItem() async { if (mc.text.isNotEmpty) { final newItem = DataModel(id: 0, name: mc.text); await DBHelper.instance.create(newItem); await refreshNames(); mc.clear(); } }
4. removeItem方法逻辑错误
原代码索引操作存在越界风险,应根据列表实际数据的ID进行删除:
removeItem() async { if (mylist.isNotEmpty) { final lastItem = mylist.last; await DBHelper.instance.delete(lastItem.id); await refreshNames(); } }
5. mylist定义为static导致状态异常
static修饰的变量会脱离组件状态管理,改为普通成员变量:
late List<DataModel> mylist = <DataModel>[];
内容的提问来源于stack exchange,提问作者DktPhl2018
相关产品推荐
相关产品推荐

