Dart中db函数返回Database?与Future<Database>返回类型不匹配报错
问题现象
开发Dart/Flutter本地数据库功能时,抛出如下类型校验错误:
A value of type 'Database?' can't be returned from the function 'db' because it has a return type of 'Future<Database>'
报错含义:类型为Database?(可空Database)的值无法从db getter返回,因为该getter声明的返回类型为非空的Future<Database>。
触发报错的是单例模式实现的sqflite数据库操作帮助类,原始代码如下:
class DatabaseHelper { static final DatabaseHelper _instance = new DatabaseHelper.internal(); factory DatabaseHelper() => _instance; static Database? _db; Future<Database> get db async { if (_db != null) { return _db; } _db = await initDb(); return _db; } DatabaseHelper.internal(); initDb() async { Directory documentDirectory = await getApplicationDocumentsDirectory(); String path = join(documentDirectory.path, "main.db"); var ourDb = await openDatabase(path, version: 1, onCreate: _onCreate); return ourDb; } void _onCreate(Database db, int version) async { await db.execute( "CREATE TABLE User(id INTEGER PRIMARY KEY, username TEXT, password TEXT)" ); print("Table is created"); } }
问题原因
该错误由Dart空安全机制的类型提升规则限制导致:
- 代码中静态字段
_db声明为可空类型Database?,而dbgetter要求返回非空的Future<Database>类型 - Dart的流分析无法对静态字段、实例字段执行判空后的类型自动提升——这类字段可能被外部逻辑在判空后、返回前修改为null,流分析无法保证其非空性,因此即使写了
if (_db != null)判断,_db依然会被识别为可空的Database?类型,和返回值要求不匹配 - 原始代码中
initDb()方法没有显式声明返回值类型,在开启严格类型校验的项目中会额外触发类型警告
修复方案
首先补全initDb()方法的返回值类型为Future<Database>,消除严格类型校验下的警告,再选择以下任意一种方式解决空类型不匹配问题:
- 方式1:使用非空断言操作符(代码改动最小)
判空后对_db添加!非空断言,明确告知编译器当前场景下_db一定不为空,修改后的dbgetter和initDb方法如下:Future<Database> initDb() async { Directory documentDirectory = await getApplicationDocumentsDirectory(); String path = join(documentDirectory.path, "main.db"); var ourDb = await openDatabase(path, version: 1, onCreate: _onCreate); return ourDb; } Future<Database> get db async { if (_db != null) { return _db!; } _db = await initDb(); return _db!; } - 方式2:使用局部变量实现类型提升(空安全最佳实践)
将静态字段赋值给局部临时变量,Dart流分析可以对局部变量正常做判空后的类型提升,避免非空断言可能带来的潜在运行时风险,修改后的代码如下:Future<Database> initDb() async { Directory documentDirectory = await getApplicationDocumentsDirectory(); String path = join(documentDirectory.path, "main.db"); var ourDb = await openDatabase(path, version: 1, onCreate: _onCreate); return ourDb; } Future<Database> get db async { final currentDb = _db; if (currentDb != null) { return currentDb; } final newDb = await initDb(); _db = newDb; return newDb; }
内容的提问来源于stack exchange,提问作者Ankita Thorat
相关产品推荐
相关产品推荐

