The non-nullable variable '_database' must be initialized
There are two problems in your code which both comes from the new Dart non-nullable by default (NNBD) feature introduced with version 2.12.0. Both problems can be found in the following segment:
static Database _database; Future<Database> get database async { if (_database == null) { _database = await _initiateDatabase(); } return _database; }
First, in Dart 2.12.0, Database
means a type which does not allow null
as value. In your case, you define a variable _database
which is not being initialized with any value. So this variable is going to have the value null
. But Database
does not allow that.
Instead, we need to use the type Database?
which allows us to point to a Database
object or null
:
static Database? _database; Future<Database> get database async { if (_database == null) { _database = await _initiateDatabase(); } return _database; }
Now we get a new problem:
A value of type 'Database?' can't be returned from the function 'database' because it has a return type of 'Future<Database>'
The reason for this is Dart null-safety feature does not promote class fields when doing if (_database == null)
. You can read more about that here and the reason why: Dart null safety doesn't work with class fields
To fix this we can rewrite your code to:
static Database? _database; Future<Database> get database async => _database ??= await _initiateDatabase();
The ??=
operator will check if _database
is null
and set it to the value of await _initiateDatabase()
if that is the case and then return the new value of _database
. If _database
already has a value, it will just be returned.
A good list of null-aware operators in Dart can be found here: https://medium.com/@thinkdigitalsoftware/null-aware-operators-in-dart-53ffb8ae80bb
You can read more about Dart non-nullable by default here:https://dart.dev/null-safety
Bonus
I think you should also change:
_initiateDatabase() async {
To:
Future<Database> _initiateDatabase() async {
Since we do not not know which type _initiateDatabase
returns and Dart will therefore assume it is dynamic
which is properly not what you want.
import 'dart:io';import 'package:path_provider/path_provider.dart';import 'package:sqflite/sqflite.dart';import 'package:path/path.dart';class DatabaseHelper { static final dbname = "myDatabase.db"; static final dbversion = 1; static final tablename = "myTable"; static final columnId = "id"; static final columnName = "name"; DatabaseHelper._privateConstructor(); static final DatabaseHelper instance = DatabaseHelper._privateConstructor(); static Database? _database; Future<Database?> get database async { if (_database != null) { return _database; } _database = await initiateDatabase(); return _database; } initiateDatabase() async { Directory directory = await getApplicationDocumentsDirectory(); String path = join(directory.path, dbname); return await openDatabase(path, version: dbversion, onCreate: onCreate); } Future onCreate(Database db, int dbversion) async { return await db.execute(''' CREATE TABLE $tablename ($columnId INTEGER PRIMARY KEY, $columnName TEXT NOT NULL) '''); } Future<int> insert(Map<String, dynamic> row) async { Database? db = await instance.database; return await db!.insert(tablename, row); } Future<List<Map<String, dynamic>>> queryAll() async { Database? db = await instance.database; return await db!.query(tablename); } Future<int> update(Map<String, dynamic> row) async { Database? db = await instance.database; int id = row[columnId]; return await db! .update(tablename, row, where: '$columnId=?', whereArgs: [id]); } Future<int> delete(int id) async { Database? db = await instance.database; return await db!.delete(tablename, where: '$columnId=?', whereArgs: [id]); }}