Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Singleton Class in Flutter with NullSafety

I have this class which takes some parameters by using the factory constructor, if instance is null, a new object will be created; if it's not null, the value of instance will be returned so we always receive the same object all the time (Singleton). This is how I used the singleton pattern before enabling null-safety features of dart.

class GuestUser extends User {
  static GeustUser _instance;


  factory GuestUser(
      {required String firstName,
      required String lastName,
      required String email,
      required Address address,
      required PaymentInfo paymentInfo}) {
    if (_instance == null) {
      _instance =
          GuestUser._(firstName, lastName, email, address, paymentInfo);
    }
    return _instance;
  }

Now with null-safety enabled, I get this error:

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

Also if (_instance == null) is not needed anymore.

If I define the _instance like this: static late final GuestUser _instance; then I cannot use the if (_instance == null) to only create the _instance when needed. so I have to remove the if statement and create a new instance every time the factory constructor is called.

How can I solve this issue and create a singleton class with null-safety enabled?

I have this solution in mind to keep track of the instance with a boolean variable:

static late final GeustUser _instance;
static bool _isInstanceCreated = false;

  factory GuestUser(
      {required String firstName,
      required String lastName,
      required String email,
      required Address address,
      required PaymentInfo paymentInfo}) {
    if (_isInstanceCreated == false) {
      _instance =
          GuestUser._(firstName, lastName, email, address, paymentInfo);
    }
    _isInsanceCreated = true;
    return _instance;
  }

But I want to know whether there is a way to do this without defining new variable and by using the features of the null-safety

like image 826
Morez Avatar asked Mar 06 '21 20:03

Morez


2 Answers

Your singleton is a little strange since your are taking arguments which will only be used the first time it is called. It is not a great pattern since it can give some surprises if e.g. your want to use it for two different guest users.

In your example it makes sense to just use GuestUser? as the type for _instance. Non-nullable by default in Dart should not be seen as the use of null is bad and you should definitely use null where it makes sense (especially if we can prevent the introduction of bool variables to indicate if a variable has been set). In your example, _instance is null until it is initialized the first time.

Example where we use the ??= operator. The operator will check if _instance is null and in case of null, it will assign the variable to the object created by calling the constructor GuestUser._. It will hereafter return the value of _instance. If _instance does already have a value (not null), this value will just be returned without calling the GuestUser._ constructor.

class GuestUser extends User {
  static GuestUser? _instance;

  factory GuestUser(
          {required String firstName,
          required String lastName,
          required String email,
          required Address address,
          required PaymentInfo paymentInfo}) =>
      _instance ??=
          GuestUser._(firstName, lastName, email, address, paymentInfo);
}

If you had a more traditional singleton, you could have made a static final variable where the instance is created in the definition. But this does not allow for parameters.

like image 154
julemand101 Avatar answered Sep 18 '22 20:09

julemand101


As @julemand101 mentioned, your singleton is actually strange, because generally you'd do something like:

class Foo {
  static final instance = Foo._();
  Foo._();
}

However, you can't instantiate it with the parameters. For that you can do this:

class Bar {
  static Bar? instance;
  Bar._(int i);

  factory Bar.fromInt(int i) {
    var bar = Bar._(i);
    instance = bar;
    return bar;
  }

  void display() => print('Bar instance');
}

void main() {
  final nullableBar = Bar.instance;
  final nonNullableBar = Bar.fromInt(0);

  nullableBar!.display(); // Runtime error as Bar.instance is used before Bar.fromMap(0)
  nonNullableBar.display(); // No error
}
like image 29
CopsOnRoad Avatar answered Sep 21 '22 20:09

CopsOnRoad