You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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字段、db getter方法及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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 16:12:04