Flutter运行报错LateInitializationError: Field 'Db'未初始化问题排查
报错原因
- 核心原因是
DbProvider的异步初始化逻辑没有等待完成就直接访问了late修饰的Db字段:DbProvider构造函数内调用的init()是异步方法,Dart中构造函数无法被标记为async,所以实例化DbProvider后代码会直接往下执行,此时init内的openDatabase异步操作还未执行完成,Db字段尚未被赋值,后续访问时就触发LateInitializationError。 - 多处重复实例化
DbProvider:Bloc类、NameTiles的build方法中各创建了一个独立的DbProvider实例,不仅重复初始化数据库造成资源浪费,也增大了初始化未完成就被访问的概率。 - 数据库操作前无初始化校验:
fetchInfoWithNameOrder等方法直接访问db.Db,没有任何逻辑判断数据库是否已经初始化完成。 - 原代码存在SQL语法错误:Att表的建表语句中
name text,后面多了一个多余的逗号,会导致onCreate执行失败,也会引发Db字段赋值失败。
修复方案
1. 将DbProvider改造为单例+异步安全获取实例
避免重复初始化,同时保证所有访问数据库的操作都等初始化完成后再执行,改造后代码如下:
import 'dart:async'; import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; import 'package:path_provider/path_provider.dart'; class DbProvider{ Database? _db; // 单例实现,全局仅一个实例 static final DbProvider _instance = DbProvider._internal(); factory DbProvider() => _instance; DbProvider._internal(); Completer<void>? _initCompleter; Future<void> init() async { // 避免重复执行初始化逻辑 if (_initCompleter != null) return _initCompleter!.future; _initCompleter = Completer<void>(); try { Directory dir = await getApplicationDocumentsDirectory(); final path = join(dir.path, 'AppDb.db'); _db = await openDatabase( path, version: 1, onCreate: (Database newDb, int version){ newDb.execute( """ create table Names( id integer primary key, name text, Salary integer, total integer ); create table Att( id integer primary key, name text ); """ ); } ); _initCompleter!.complete(); } catch (e) { _initCompleter!.completeError(e); } return _initCompleter!.future; } // 暴露安全的数据库实例获取方法 Future<Database> get db async { if (_db == null) { await init(); } return _db!; } }
2. 修改Bloc中的数据库调用逻辑
所有访问数据库的位置先异步等待获取数据库实例:
fetchInfo(String arg) async{ print('\nfetchInfo\n'); // 等待数据库初始化完成后再获取实例 final dbInstance = await db.db; var ans = await dbInstance.query( "Names", where: "name = ?", whereArgs: [arg], ); print(ans); addToTile(ans); } fetchInfoWithNameOrder()async{ print('\nfetchinfowithorder\n'); final dbInstance = await db.db; var ans = await dbInstance.query( "Names", orderBy: "name asc", ); print(ans); addToTile(ans); }
3. 清理冗余代码
删除NameTiles类build方法中冗余的final db = DbProvider();实例化代码,全局统一使用Bloc中持有的单例即可。
可选优化
可在应用启动入口(如main函数中runApp之前)提前执行await DbProvider().init();预加载数据库,减少首次业务调用的等待耗时。
内容的提问来源于stack exchange,提问作者sharmaji saheb
相关产品推荐
相关产品推荐

