Flutter sqflite存储数据重启后丢失问题求助
用药提醒App重启后数据丢失问题排查与修复
核心问题梳理
开发用药提醒App时使用sqflite存储数据,但每次重启App后数据丢失,手动判断数据库存在性的逻辑未生效,导致每次重启都重新创建数据库。以下是具体错误点及修复方案:
1. 未触发本地数据加载流程
MedicationProvider中定义了init()方法用于从数据库加载已有数据,但在App启动时从未调用该方法,导致每次重启后Provider的本地列表始终为空,无法展示已存储的数据。
修复方案:
在首页MedicationScreen的initState中调用Provider的初始化方法:
class MedicationScreenState extends State<MedicationScreen> { @override void initState() { super.initState(); // 加载本地数据库数据 Provider.of<MedicationProvider>(context, listen: false).init(); } // 其他代码... }
2. 数据库初始化逻辑冗余且错误
sqflite的openDatabase方法本身会自动处理数据库的创建与打开:若数据库不存在则执行onCreate创建,若已存在则直接打开。手动添加databaseExists判断完全多余,甚至可能因路径判断误差导致误判,触发重复创建逻辑。
修复方案:
简化DatabaseHelper的_initDatabase方法:
Future<Database> _initDatabase() async { final String path = await getDatabasesPath(); final String dbPath = join(path, 'medications.db'); // 移除多余的存在性判断,交由openDatabase自动处理 return openDatabase( dbPath, version: 1, onCreate: _createDatabase, ); }
3. 数据操作顺序与参数不匹配
(1)新增数据逻辑顺序错误
当前addMedication先更新本地列表再插入数据库,若数据库插入失败,会导致本地列表与数据库数据不一致。且方法返回void但内部使用await,易引发异步执行问题。
修复方案:
先完成数据库插入,再更新本地列表:
Future<void> addMedication(Medication medication) async { final db = await database.database; // 获取插入后的自增ID final insertedId = await db.insert('medications', medication.toMap()); // 更新Medication的ID(需确保模型有id字段及copyWith方法) final updatedMed = medication.copyWith(id: insertedId); _medications.add(updatedMed); notifyListeners(); }
(2)更新/删除数据的参数不匹配
updateMedication中,数据库where条件使用主键id,但whereArgs传入的是notificationIDs(非主键字段,类型可能不匹配),导致更新操作失效。deleteMedication中,参数为String id,但数据库主键是integer类型,类型不匹配导致删除操作失效,且本地列表删除逻辑依赖notificationIDs而非数据库主键,导致数据不同步。
修复方案:
修改更新与删除逻辑,统一使用数据库主键id:
// 更新数据 Future<void> updateMedication(Medication medication) async { final db = await database.database; await db.update( 'medications', medication.toMap(), where: 'id = ?', whereArgs: [medication.id], // 使用数据库主键 ); final index = _medications.indexWhere((e) => e.id == medication.id); if (index != -1) { _medications[index] = medication; notifyListeners(); } } // 删除数据 Future<void> deleteMedication(int id) async { // 参数改为int类型 final db = await database.database; await db.delete( 'medications', where: 'id = ?', whereArgs: [id], ); _medications.removeWhere((e) => e.id == id); notifyListeners(); }
4. 模型字段映射一致性检查
确保Medication的toMap()/fromMap()方法与数据库表字段完全匹配:
- 数据库主键
id为integer类型,模型中需对应int类型字段 - 避免将非数据库字段(如
notificationIDs)写入toMap(),防止插入/更新时出现无效字段报错
内容的提问来源于stack exchange,提问作者Muhammad AsfandYar Amjad
相关产品推荐
相关产品推荐

