Search code examples
sqldatabaseflutterdartsqflite

The non-nullable variable '_database' must be initialized


I am using flutter to make a Windows app and while using the sqflite and making a database, this error pops up I don't know how to really fix this.

import 'dart:io';

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

class DatabaseHelper {
  static final _dbName = 'Database.db';
  static final _dbVersion = 1;
  static final _tableName = 'my table';

  static final columnId = '_id';
  static final columnName = 'name';

  DatabaseHelper._privateConstuctor();
  static final DatabaseHelper instance = DatabaseHelper._privateConstuctor();

  static Database _database;
  Future<Database> get database async {
    if (_database == null) {
      _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 version) async {
    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]);
  }
}

This is the code i use for the databasehelper....it shows error in _database like this:

The non-nullable variable '_database' must be initialized.
Try adding an initializer expression.

Solution

  • 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.