Flutter DatabaseHelper类初始化错误排查及Firestore同步函数检查
Flutter DatabaseHelper 问题排查与修复
问题描述
- 初始错误:
DatabaseHelper._internal();行报错「Non-nullable instance field '_db' must be initialized」,要求添加初始化器或标记为late。 - 修改后错误:将
Database _db;改为late Database _db;后,触发LateInitializationError: Field '_db@382359948' has not been initialized,错误栈指向_db字段、dbgetter方法及main函数。
错误日志
Non-nullable instance field '_db' must be initialized. (Documentation) Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'. If I make `Database _db;` to `late Database _db;` then it shows me this error: E/flutter (10954): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: LateInitializationError: Field '_db@382359948' has not been initialized. E/flutter (10954): #0 DatabaseHelper._db (package:recipedia/Database/databaseHelper.dart) E/flutter (10954): #1 DatabaseHelper.db (package:recipedia/Database/databaseHelper.dart:177:9) E/flutter (10954): #2 main (package:recipedia/main.dart:18:18) E/flutter (10954): <asynchronous suspension>
相关代码
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:sqflite/sqflite.dart'; class DatabaseHelper { final String recipeTable = 'recipes'; final String recipeId = 'recipeId'; final String recipeName = 'recipeName'; final String recipeCategory = 'recipeCategory'; final String recipeDescription = 'recipeDescription'; final String recipeRating = 'recipeRating'; final String recipeTime = 'recipeTime'; final String recipeIngredients = 'recipeIngredients'; final String recipeURL = 'recipeURL'; static final DatabaseHelper _instance = DatabaseHelper._internal(); factory DatabaseHelper() => _instance; DatabaseHelper._internal(); Database _db; Future<Database> get db async { if (_db == null) { _db = await _initDatabase(); } return _db; } Future<Database> _initDatabase() async { final String path = await getDatabasesPath(); return openDatabase( '$path/recipes.db', version: 1, onCreate: (db, version) { db.execute( 'CREATE TABLE $recipeTable($recipeId INTEGER PRIMARY KEY, $recipeName TEXT, $recipeDescription TEXT, $recipeCategory TEXT, $recipeIngredients TEXT, $recipeURL TEXT, $recipeTime TEXT, $recipeRating INTEGER)'); }, ); } Future<void> syncDataFromFirestore() async { final QuerySnapshot<Map<String, dynamic>> snapshot = await FirebaseFirestore.instance.collection('recipes').get(); final Batch batch = _db.batch(); for (final QueryDocumentSnapshot<Map<String, dynamic>> doc in snapshot.docs) { final Map<String, dynamic> data = doc.data(); batch.insert( recipeTable, { recipeId: doc.id, recipeName: data['recipe_name'], recipeCategory: data['recipe_category'], recipeDescription: data['recipe_description'], recipeRating: data['recipeRating'], recipeTime: data['recipeTime'], recipeIngredients: data['recipeIngredients'], recipeURL: data['recipeURL'], }, ); } await batch.commit(); } Future<void> syncData() async {...} Future<List<int>> getAllRecipeID() async {...} Future<List<Map<String, dynamic>>> getAllRecipe() async {...} Future<List<Map<String, dynamic>>> getRecipe(int id) async {...} Future<int> updateRecipe(Map<String, dynamic> recipe) async {...} Future<int> deleteRecipe(int id) async {...} }
问题修复与代码优化
1. 解决_db初始化问题
原代码中_db是非空类型但未初始化,改成late后又因直接访问_db(未通过异步db getter)导致未初始化错误。推荐采用可空类型+空赋值运算符的方案,既符合空安全规范,又能确保初始化逻辑正确:
// 替换原有的Database _db; Database? _db; Future<Database> get db async { _db ??= await _initDatabase(); return _db!; }
2. 修复syncDataFromFirestore()函数错误
函数中直接使用_db.batch()会跳过初始化逻辑,必须通过await db获取已初始化的数据库实例:
Future<void> syncDataFromFirestore() async { final QuerySnapshot<Map<String, dynamic>> snapshot = await FirebaseFirestore.instance.collection('recipes').get(); // 先获取初始化后的数据库实例 final Database dbInstance = await db; final Batch batch = dbInstance.batch(); for (final QueryDocumentSnapshot<Map<String, dynamic>> doc in snapshot.docs) { final Map<String, dynamic> data = doc.data(); batch.insert( recipeTable, { recipeId: doc.id, recipeName: data['recipe_name'], recipeCategory: data['recipe_category'], recipeDescription: data['recipe_description'], recipeRating: data['recipeRating'], recipeTime: data['recipeTime'], recipeIngredients: data['recipeIngredients'], recipeURL: data['recipeURL'], }, ); } await batch.commit(); }
3. 额外注意事项
- 确保在
main函数中调用数据库操作前,先执行await DatabaseHelper().db完成初始化,避免未初始化错误。 - 核对Firestore字段名与本地数据库字段名的映射,确保所有字段对应关系正确。
内容的提问来源于stack exchange,提问作者Ahmed Raza
相关产品推荐
相关产品推荐

