[Dart Design Pattern] Multiple ways to implement Singleton Pattern in Dart

Singleton Pattern in Dart

In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one “single” instance. This is useful when exactly one object is needed to coordinate actions across the system. The term comes from the mathematical concept of a singleton.

You should be aware of Singletons in multi-threaded applications. If they hold some kind of mutable data, it could lead to unexpected results, so the synchronization mechanism should be considered.

Since we are talking about the Dart programming language in this series, you should know that Dart is a single-threaded programming language and its code runs in a little isolated space on the machine, called isolate. Hence, you should not worry about the thread-safety when implementing Singletons in Dart as long as you do not create a new separate isolate from the code by yourself.

1. Static Field

In the following example, the static field instance is a singleton instance.

1
2
3
4
5
6
7
8
9
10
11
// main.dart

class Singleton {
Singleton._privateConstructor();

static final Singleton instance = Singleton._privateConstructor();
}

void main() {
print("identical = ${identical(Singleton.instance, Singleton.instance)}");
}

It will output:

1
identical = true

2. Static Field with Getter

In the following example, the static getter returns a singleton instance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// main.dart

class Singleton {
Singleton._privateConstructor();

static final Singleton _instance = Singleton._privateConstructor();

static Singleton get instance {
return _instance;
}
}

void main() {
print("identical = ${identical(Singleton.instance, Singleton.instance)}");
}

It will output:

1
identical = true

3. Factory Constructor

In the following example, the factory constructor returns a singleton instance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// main.dart

class Singleton {
Singleton._privateConstructor();

static final Singleton _instance = Singleton._privateConstructor();

factory Singleton() {
return _instance;
}
}

void main() {
print("identical = ${identical(Singleton(), Singleton())}");
}

It will output:

1
identical = true

4. Const Constructor

4.1 Const Constructor with Factory Constructor

In the following example, the factory constructor returns a const instance, and it is a singleton instance too.

1
2
3
4
5
6
7
8
class Singleton {
factory Singleton() => const Singleton._internal_();
const Singleton._internal_();
}

void main() {
print("identical = ${identical(Singleton(), Singleton())}");
}

It will output:

1
identical = true

4.2 Const Constructor with const modifier

In the following example, const constructor with const modifier initialize a const instance, and it is a singleton instance too.

1
2
3
4
5
6
7
8
class Singleton {
const Singleton();
}

void main() {
print("without const identical = ${identical(Singleton(), Singleton())}"); // It will initialize different instance without `const` modifier.
print("with const identical = ${identical(const Singleton(), const Singleton())}"); // With const modifier
}

It will output:

1
2
without const identical = false
with const identical = true

References

[1] Constructors - Language tour | Dart - https://dart.dev/guides/language/language-tour#constructors

[2] Singleton pattern - Wikipedia - https://en.wikipedia.org/wiki/Singleton_pattern

[3] identical function - dart:core library - Dart API - https://api.dart.dev/stable/2.5.1/dart-core/identical.html

[4] Flutter Design Patterns: 1 — Singleton | by Mangirdas Kazlauskas | Flutter Community | Medium - https://medium.com/flutter-community/flutter-design-patterns-1-singleton-437f04e923ce