12

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.
julemand101
  • 28,470
  • 5
  • 52
  • 48
sanjay .k.santhosh
  • 123
  • 1
  • 1
  • 4
  • Your variable `static Database _database;` is not assigned any value when initialized. So the value are going to be `null`. But because you are using Dart 2.12, the type `Database` is a non-nullable type so it can never be `null`. If you want to allow it to have the value `null`, you should change the type to `Database?` which allows the variable to point to a `Database` object or `null`. – julemand101 Apr 11 '21 at 22:34
  • thankyou it worked but now it is showing "A value of type 'Database?' can't be returned from the function 'database' because it has a return type of 'Future'". – sanjay .k.santhosh Apr 12 '21 at 03:40
  • Does this answer your question? [Non-nullable instance field must be initialized](https://stackoverflow.com/questions/67034475/non-nullable-instance-field-must-be-initialized) – iDecode Jul 09 '21 at 08:18

4 Answers4

64

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.

julemand101
  • 28,470
  • 5
  • 52
  • 48
2

Maybe this will help you, please flutter sdk change as as follows

sdk: ">=2.12.0 <3.0.0" => sdk: ">=2.7.0 <3.0.0"

Taryn East
  • 27,486
  • 9
  • 86
  • 108
方世玉
  • 29
  • 1
2
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]);
  }
}
  • 1
    Please [edit] your answer and explain what this code does, for more clarification. Read [answer] and take the [tour] to learn how this site works. – padaleiana Jul 22 '21 at 18:06
1

In my opinion, the best solution is to do the following, as it checks the _database variable is not null before it is used:

  static Database? _database;
  Future<Database?> get database async {
  if (_database != null) return _database;
  _database = await _databaseConnection.setDatabase();
  return _database;
  } 
  • While this code may solve the question, [including an explanation](//meta.stackexchange.com/q/114762) of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please [edit] your answer to add explanations and give an indication of what limitations and assumptions apply. – Adrian Mole Sep 09 '22 at 17:37