使用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文件。
- 卸载App后重新运行,查看
内容的提问来源于stack exchange,提问作者borhanedine
相关产品推荐
相关产品推荐

