The singleton pattern ensures only one instance of a class is ever created. How do I build this in Dart?
22 Answers
Thanks to Dart's factory constructors, it's easy to build a singleton:
class Singleton {
static final Singleton _singleton = Singleton._internal();
factory Singleton() {
return _singleton;
}
Singleton._internal();
}
You can construct it like this
main() {
var s1 = Singleton();
var s2 = Singleton();
print(identical(s1, s2)); // true
print(s1 == s2); // true
}
Here is a comparison of several different ways to create a singleton in Dart.
1. Factory constructor
class SingletonOne {
SingletonOne._privateConstructor();
static final SingletonOne _instance = SingletonOne._privateConstructor();
factory SingletonOne() {
return _instance;
}
}
2. Static field with getter
class SingletonTwo {
SingletonTwo._privateConstructor();
static final SingletonTwo _instance = SingletonTwo._privateConstructor();
static SingletonTwo get instance => _instance;
}
3. Static field
class SingletonThree {
SingletonThree._privateConstructor();
static final SingletonThree instance = SingletonThree._privateConstructor();
}
How to instantiate
The above singletons are instantiated like this:
SingletonOne one = SingletonOne();
SingletonTwo two = SingletonTwo.instance;
SingletonThree three = SingletonThree.instance;
Note:
I originally asked this as a question, but discovered that all of the methods above are valid and the choice largely depends on personal preference.
I don't find it very intuitive reading new Singleton()
. You have to read the docs to know that new
isn't actually creating a new instance, as it normally would.
Here's another way to do singletons (Basically what Andrew said above).
lib/thing.dart
library thing;
final Thing thing = new Thing._private();
class Thing {
Thing._private() { print('#2'); }
foo() {
print('#3');
}
}
main.dart
import 'package:thing/thing.dart';
main() {
print('#1');
thing.foo();
}
Note that the singleton doesn't get created until the first time the getter is called due to Dart's lazy initialization.
If you prefer you can also implement singletons as static getter on the singleton class. i.e. Thing.singleton
, instead of a top level getter.
Also read Bob Nystrom's take on singletons from his Game programming patterns book.
What about just using a global variable within your library, like so?
single.dart
:
library singleton;
var Singleton = new Impl();
class Impl {
int i;
}
main.dart
:
import 'single.dart';
void main() {
var a = Singleton;
var b = Singleton;
a.i = 2;
print(b.i);
}
Or is this frowned upon?
The singleton pattern is necessary in Java where the concept of globals doesn't exist, but it seems like you shouldn't need to go the long way around in Dart.
Here is a simple answer:
first of all, we need a private
and static
property of class type.
secondly, the constructor should be private
, because we want to prevent object initialization from outside of the class.
and finally, we check instance nullability, if it is null we will instantiate and return it, else we will return the already instantiated instance.
class Singleton {
static Singleton? _instance;
Singleton._();
static Singleton get getInstance => _instance ??= Singleton._();
}
Here is another possible way:
void main() {
var s1 = Singleton.instance;
s1.somedata = 123;
var s2 = Singleton.instance;
print(s2.somedata); // 123
print(identical(s1, s2)); // true
print(s1 == s2); // true
//var s3 = new Singleton(); //produces a warning re missing default constructor and breaks on execution
}
class Singleton {
static final Singleton _singleton = new Singleton._internal();
Singleton._internal();
static Singleton get instance => _singleton;
var somedata;
}
Singleton that can't change the object after the instantiation
class User {
final int age;
final String name;
User({
this.name,
this.age
});
static User _instance;
static User getInstance({name, age}) {
if(_instance == null) {
_instance = User(name: name, age: age);
return _instance;
}
return _instance;
}
}
print(User.getInstance(name: "baidu", age: 24).age); //24
print(User.getInstance(name: "baidu 2").name); // is not changed //baidu
print(User.getInstance()); // {name: "baidu": age 24}
Modified @Seth Ladd answer for who's prefer Swift style of singleton like .shared
:
class Auth {
// singleton
static final Auth _singleton = Auth._internal();
factory Auth() => _singleton;
Auth._internal();
static Auth get shared => _singleton;
// variables
String username;
String password;
}
Sample:
Auth.shared.username = 'abc';
In this example I do other things that are also necessary when wanting to use a Singleton. For instance:
- pass a value to the singleton's constructor
- initialize a value inside the constructor itself
- set a value to a Singleton's variable
- be able to access and access those values.
Like this:
class MySingleton {
static final MySingleton _singleton = MySingleton._internal();
String _valueToBeSet;
String _valueAlreadyInSingleton;
String _passedValueInContructor;
get getValueToBeSet => _valueToBeSet;
get getValueAlreadyInSingleton => _valueAlreadyInSingleton;
get getPassedValueInConstructor => _passedValueInContructor;
void setValue(newValue) {
_valueToBeSet = newValue;
}
factory MySingleton(String passedString) {
_singleton._valueAlreadyInSingleton = "foo";
_singleton._passedValueInContructor = passedString;
return _singleton;
}
MySingleton._internal();
}
Usage of MySingleton:
void main() {
MySingleton mySingleton = MySingleton("passedString");
mySingleton.setValue("setValue");
print(mySingleton.getPassedValueInConstructor);
print(mySingleton.getValueToBeSet);
print(mySingleton.getValueAlreadyInSingleton);
}
Since Dart 2.13
version, it is very easy with late
keyword. Late
keyword allows us to lazily instantiate objects.
As an example, you can see it:
class LazySingletonExample {
LazySingletonExample._() {
print('instance created.');
}
static late final LazySingletonExample instance = LazySingletonExample._();
}
Note: Keep in mind that, it will only be instantiated once when you call lazy
instance
field.
Here's a concise example that combines the other solutions. Accessing the singleton can be done by:
- Using a
singleton
global variable that points to the instance. - The common
Singleton.instance
pattern. - Using the default constructor, which is a factory that returns the instance.
Note: You should implement only one of the three options so that code using the singleton is consistent.
Singleton get singleton => Singleton.instance;
ComplexSingleton get complexSingleton => ComplexSingleton._instance;
class Singleton {
static final Singleton instance = Singleton._private();
Singleton._private();
factory Singleton() => instance;
}
class ComplexSingleton {
static ComplexSingleton _instance;
static ComplexSingleton get instance => _instance;
static void init(arg) => _instance ??= ComplexSingleton._init(arg);
final property;
ComplexSingleton._init(this.property);
factory ComplexSingleton() => _instance;
}
If you need to do complex initialization, you'll just have to do so before using the instance later in the program.
Example
void main() {
print(identical(singleton, Singleton.instance)); // true
print(identical(singleton, Singleton())); // true
print(complexSingleton == null); // true
ComplexSingleton.init(0);
print(complexSingleton == null); // false
print(identical(complexSingleton, ComplexSingleton())); // true
}
If you happen to be using Flutter and provider
package for state management, creating and using a singleton is quite straightforward.
- Create instance
void main() { runApp( MultiProvider( providers: [ ChangeNotifierProvider(create: (context) => SomeModel()), Provider(create: (context) => SomeClassToBeUsedAsSingleton()), ], child: MyApp(), ), ); }
- Get instance
Widget build(BuildContext context) { var instance = Provider.of<SomeClassToBeUsedAsSingleton>(context); ...
This is how I implement singleton in my projects
Inspired from flutter firebase => FirebaseFirestore.instance.collection('collectionName')
class FooAPI {
foo() {
// some async func to api
}
}
class SingletonService {
FooAPI _fooAPI;
static final SingletonService _instance = SingletonService._internal();
static SingletonService instance = SingletonService();
factory SingletonService() {
return _instance;
}
SingletonService._internal() {
// TODO: add init logic if needed
// FOR EXAMPLE API parameters
}
void foo() async {
await _fooAPI.foo();
}
}
void main(){
SingletonService.instance.foo();
}
example from my project
class FirebaseLessonRepository implements LessonRepository {
FirebaseLessonRepository._internal();
static final _instance = FirebaseLessonRepository._internal();
static final instance = FirebaseLessonRepository();
factory FirebaseLessonRepository() => _instance;
var lessonsCollection = fb.firestore().collection('lessons');
// ... other code for crud etc ...
}
// then in my widgets
FirebaseLessonRepository.instance.someMethod(someParams);
Hello what about something like this? Very simple implementation, Injector itself is singleton and also added classes into it. Of course can be extended very easily. If you are looking for something more sophisticated check this package: https://pub.dartlang.org/packages/flutter_simple_dependency_injection
void main() {
Injector injector = Injector();
injector.add(() => Person('Filip'));
injector.add(() => City('New York'));
Person person = injector.get<Person>();
City city = injector.get<City>();
print(person.name);
print(city.name);
}
class Person {
String name;
Person(this.name);
}
class City {
String name;
City(this.name);
}
typedef T CreateInstanceFn<T>();
class Injector {
static final Injector _singleton = Injector._internal();
final _factories = Map<String, dynamic>();
factory Injector() {
return _singleton;
}
Injector._internal();
String _generateKey<T>(T type) {
return '${type.toString()}_instance';
}
void add<T>(CreateInstanceFn<T> createInstance) {
final typeKey = _generateKey(T);
_factories[typeKey] = createInstance();
}
T get<T>() {
final typeKey = _generateKey(T);
T instance = _factories[typeKey];
if (instance == null) {
print('Cannot find instance for type $typeKey');
}
return instance;
}
}
** Sigleton Paradigm in Dart Sound Null Safety**
This code snippet shows how to implement singleton in dart This is generally used in those situation in which we have to use same object of a class every time for eg. in Database transactions.
class MySingleton {
static MySingleton? _instance;
MySingleton._internal();
factory MySingleton() {
if (_instance == null) {
_instance = MySingleton._internal();
}
return _instance!;
}
}
As I'm not very fond of using the new
keyword or other constructor like calls on singletons, I would prefer to use a static getter called inst
for example:
// the singleton class
class Dao {
// singleton boilerplate
Dao._internal() {}
static final Dao _singleton = new Dao._internal();
static get inst => _singleton;
// business logic
void greet() => print("Hello from singleton");
}
example usage:
Dao.inst.greet(); // call a method
// Dao x = new Dao(); // compiler error: Method not found: 'Dao'
// verify that there only exists one and only one instance
assert(identical(Dao.inst, Dao.inst));
You can just use the Constant constructors.
class Singleton {
const Singleton(); //Constant constructor
void hello() { print('Hello world'); }
}
Example:
Singleton s = const Singleton();
s.hello(); //Hello world
According with documentation:
Constant constructors
If your class produces objects that never change, you can make these objects compile-time constants. To do this, define a const constructor and make sure that all instance variables are final.