Dart compiler doesn’t recognize if a variable is non-null value

eye-catchDart and Flutter

Dart compiler doesn’t recognize even though null check logic exists before using the variable. It took me a while to understand that it’s not possible to write it the same way as TypeScript for example.

My dev environment was following.

$ dart --version
Dart SDK version: 2.13.4 (stable) (Wed Jun 23 13:08:41 2021 +0200) on "windows_x64"
Sponsored links

Dart compiler still treats a variable as nullable after null check

The code that I first wanted to write is something like this. A member is nullable and a getter does null check but Dart compiler complains that it is a nullable value.

class NullTest {
  int? _maybeInt;

  int get maybeInt {
    if (_maybeInt != null) {
      return _maybeInt; // error
      // A value of type 'int?' can't be returned from the function 'maybeInt'
      // because it has a return type of 'int'.dart(return_of_invalid_type)
    }
    return 0;
  }
}

This is very strange for me because the variable is definitely not null. However, its null check doesn’t work as expected in Dart, so we need to write in a different way.

Let’s see another example before finding solutions. The following code works as expected.

int _func(int? num) {
    if (num != null) {
        return num;
    }
    return 0;
}

The argument accepts null. The function checks whether it’s null or not and Dart compiler understands that the num variable is not null after the null check. Why…?

Using exclamation mark (bang operator)

The simplest solution is adding an exclamation mark at the end of the variable.

int get maybeInt {
    if (_maybeInt != null) {
        return _maybeInt!;
    }
    return 0;
}

It tells the Dart compiler that the variable isn’t null at runtime. It’s no problem in this simple case above. It’s obviously not null there. I want to avoid using this bang operator because it causes a runtime error and the application crashes. I wish no one uses the bang operator just in order to resolve the compile error.

Using temp variable and do null check

This way might be better than the previous one.

int get maybeInt2 {
  final temp = _maybeInt;
  if (temp != null) {
    return temp;
  }
  return 0;
}

I guess the internal process is basically the same as the one for an example _func function shown above. Dart compiler can understand temp variable is not null.

Double question mark operator

If it is simple logic we can use a double question mark operator for it.

int get maybeInt3 {
  return _maybeInt ?? 0;
}

If _maybeInt is null it returns 0, otherwise, it returns the value of _maybeInt.

Which way should I use for Singleton

What I actually wanted to do is following.

class DatabaseHolder {
  static DatabaseManager? _instance;
  DatabaseHolder._internal();

  static DatabaseManager get instance {
    if (DatabaseHolder._instance == null) {
      DatabaseHolder._instance = DatabaseManager();
    }
    return DatabaseHolder._instance!;
  }

  static set instance(DatabaseManager value) {
    DatabaseHolder._instance = value;
  }
}

I want to create a singleton class to have only one database instance. I’m not sure how I can stub functions in Dart/Flutter. Therefore, I wrote actual singleton logic in another class. In this way, I can inject a fake object via setter and I can implement the application without database-related code.
The new instance is assigned to _instance variable when it’s null but the Dart compiler thinks that the variable is nullable when it’s returned. The best choice for me, in this case, is using the bang operator because it’s simple enough and only one additional letter.

We can assign DatabaseManager instance at the line.

static DatabaseManager _instance = DatabaseManager();

This way isn’t suitable if we don’t want to call the constructor of DatabaseManager before we actually use it.

Comments

Copied title and URL