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

使用sqflite初始化数据库失败,_db为null引发插入异常的问题求助

问题描述

使用sqflite开发笔记App时,在main函数中调用数据库初始化函数后,_db始终为null,控制台能打印Database is about to be created,但openDatabase的onCreate回调中的creating db ..未打印。点击调用addTask方法的按钮时,出现Null check operator used on a null value异常。

相关代码如下:

import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

class DBHelper {
  Database? _db;

  Future<void> initializeDatabase() async {
    print('Database is about to be created');
    try {
      final String path = join(await getDatabasesPath(), 'tasks.db');
      _db = await openDatabase(
        path,
        version: 1,
        onCreate: (Database db, int version) async {
          print('creating db ..');
          await db.execute('''
          CREATE TABLE tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            taskTitle TEXT,
            taskDescription TEXT,
            isCompleted INTEGER
          )
          ''');
        },
      );
    } catch (e) {
      print('Error while initializing database: $e');
    }
  }

  Future<int> addTask(Task task) async {
    print('task is about to be inserted ...');
    return await _db!.insert('tasks', Task.toMap(task));
  }

  Future<List<Map<String, Object?>>> fetchingDataFromDB() async {
    return await _db!.query('tasks');
  }
}

// main函数代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:simple_db_app/controller/bindings.dart';
import 'package:simple_db_app/db/db_helper.dart';
import 'package:simple_db_app/view/home_page.dart';
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  DBHelper dbhelper = DBHelper();
  await dbhelper.initializeDatabase();
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      getPages: [
        GetPage(
            name: '/homePage', page: () => HomePage(), binding: TasksBidings())
      ],
      home: HomePage(),
    );
  }
}
问题原因分析
  • 数据库实例不共享:main函数中创建的DBHelper实例和页面(通过GetX Binding)使用的实例不是同一个。main里初始化了一个实例,但页面中可能通过Binding创建了新的DBHelper对象,这个新对象的_db从未被初始化,调用addTask时自然触发空指针异常。
  • onCreate未触发的可能原因:openDatabase的onCreate仅在数据库文件首次创建时执行。如果App之前已创建过tasks.db(比如之前调试未卸载),再次启动不会触发onCreate,所以不会打印creating db ..。但这种情况下_db应正常初始化,因此核心问题还是实例不共享。
  • 未做空安全检查:addTask和fetchingDataFromDB中直接使用_db!强制解包,未判断_db是否初始化,一旦实例未初始化就会抛出空指针异常。
解决方案
  • 将DBHelper改为单例模式:确保全局只有一个DBHelper实例,初始化后所有地方调用的都是同一个对象。修改代码如下:
class DBHelper {
  static final DBHelper _instance = DBHelper._internal();
  Database? _db;

  factory DBHelper() => _instance;

  DBHelper._internal();

  Future<void> initializeDatabase() async {
    print('Database is about to be created');
    try {
      final String path = join(await getDatabasesPath(), 'tasks.db');
      _db = await openDatabase(
        path,
        version: 1,
        onCreate: (Database db, int version) async {
          print('creating db ..');
          await db.execute('''
          CREATE TABLE tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            taskTitle TEXT,
            taskDescription TEXT,
            isCompleted INTEGER
          )
          ''');
        },
      );
    } catch (e) {
      print('Error while initializing database: $e');
    }
  }

  // 添加getter方法确保DB已初始化
  Future<Database> get db async {
    if (_db != null) return _db!;
    await initializeDatabase();
    return _db!;
  }

  Future<int> addTask(Task task) async {
    print('task is about to be inserted ...');
    final database = await db;
    return await database.insert('tasks', Task.toMap(task));
  }

  Future<List<Map<String, Object?>>> fetchingDataFromDB() async {
    final database = await db;
    return await database.query('tasks');
  }
}
  • 修改实例获取方式:
    main函数中改为:
    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      await DBHelper().initializeDatabase();
      runApp(MyApp());
    }
    
    在TasksBidings中,获取DBHelper实例时使用DBHelper()单例,而非创建新实例。
  • 调试验证:
    • 卸载App后重新运行,查看creating db ..是否打印,确认onCreate是否触发。
    • 在initializeDatabase中打印path值,检查数据库文件路径是否正确,也可通过文件管理器查看该路径下是否生成tasks.db文件。

内容的提问来源于stack exchange,提问作者borhanedine

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 19:40:38