Seth Ladd
Seth Ladd

Reputation: 120439

How do you build a Singleton in Dart?

The singleton pattern ensures only one instance of a class is ever created. How do I build this in Dart?

Upvotes: 538

Views: 326140

Answers (30)

Yuriy N.
Yuriy N.

Reputation: 6067

Singletons are fine. Almost every package in the pub.dev exposes itself as a singleton. Just several examples from the top of my head: Hive, Firebase, AppLinks. Use them, no worries. Some testability notes are below.

Attempts to create "lazy" singletons are meaningless as Dart has a lazy initialization feature for global and static fields. So, any singleton above and below is lazy.

Ways to create singleton.

Global variable:

var myService = _FirebaseService();
class _FirebaseService implements StorageService{
   void save(){
      //save to firebase
   }
}

Usage: myService.save();

Advantages: the shortest of all.

Disadvantages: Since myService is just a variable it does not provide exact information about the instance of which class it refers to.

Factory constructor

   class FirebaseService implements StorageService{
     FirebaseService._();
     static final _instance = FirebaseService._();
     factory FirebaseService() {
       return _instance;  
     } 
     void save(){
          //save to Firebase
     }
  }

Usage: FirebaseService().save();

Advantages: allows to send parameters into factory constructor; does not expose the fact that it is a singleton and implementation (from singleton to something else) can be changed without affecting the class client.

Static getter

  class FirebaseService implements StorageService{
     FirebaseService._();
     static final _instance = FirebaseService._();
     static FirebaseService get instance { return _instance;}
     void save(){
          //save to firebase
     }
  }

Usage: FirebaseService.instance.save();

Advantages: Firebase plugins are written this way (or the way below). We are in a good company with famous Google engineers.

Static field

  class FirebaseService implements StorageService{
     FirebaseService._();
     static final instance = FirebaseService._();
     void save(){
          //save to firebase
     }
  }

Usage: FirebaseService.instance.save();

Advantages: Shorter than the above two.

So, which of the four? My preference is the number two - factory constructor. Reasons: usage is shorter and cleaner than others; better decoupling - all the client knows is the name of the class, it doesn't even know that it is a singleton. Also, most of the packages I have used so far, are written this way.

Testability

It is known that singletons make their clients hard to test.

The solution is to implement an interface (StorageService) and inject singleton, for example through the constructor of the client:

class StorageController {
   final StorageService service; 
   StorageController ({required this.service});
}

var controller = StorageController(test ? MockService() : FirebaseService()); 

Static methods

Class with static methods is easier (shorter) to write than the singleton:

class FirebaseStorage {
   static void save() {
      //save to firebase
   } 
} 

Usage: FirebaseStorage.save();

so, we should consider using them instead when it makes sense.

Upvotes: 0

Talat El Beick
Talat El Beick

Reputation: 531

There are several ways to create a Singleton object each one of them shine for specific using.

I will show you how to create a Singleton and Which/Why approach to use.

Static field

The simplest way to use Singleton design pattern, is to create a static variable which point to an internal constructor. This way you are always getting the same object since you cannot instantiate object using the construct.

When/Why to use?

Use this approach any time you need a Singleton and you are not passing anything to it.

class Singleton {
  Singleton._internal();

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

How to instantiate?

final Singleton object1 = Singleton.instance;
final Singleton object2 = Singleton.instance;

// How to make sure they are the same? You do not need to check they are
// always the same but just to know how to do it.
// using identical Function. 

print(identical(object1, object2)); // true

Static field with method/getter

Similar to the static variable, here we're exposing the instance variable using a getter.

When/Why to use?

As I told you before it's same as static variable so it is a matter of style.

1. Method style option

class Singleton {
  Singleton._internal();

  static Singleton instance() => _instance;
  static final Singleton _instance = Singleton._internal();
}

How to instantiate?

final Singleton object1 = Singleton.instance();
final Singleton object2 = Singleton.instance();

// How to make sure they are the same? You do not need to check they are
// always the same but just to know how to do it.
// using identical Function. 

print(identical(object1, object2)); // true

2. Getter style option

class Singleton {
  Singleton._internal();

  static Singleton get instance => _instance;
  static final Singleton _instance = Singleton._internal();
}

How to instantiate?

final Singleton object1 = Singleton.instance;
final Singleton object2 = Singleton.instance;

// How to make sure they are the same? You do not need to check they are
// always the same but just to know how to do it.
// using identical Function. 

print(identical(object1, object2)); // true

Factory constructor

As previous examples we still need the static variable, but this time it will be private, since we will return it from the factory construct.

When/Why to use?

Well it's style matter on the first option, on the second option is something more.

Use this approach if you have some parameters to pass to the object, you could instantiate the object based on those parameters, for example you could have two subclasses of the Singleton so you want to determine which one to return.

1. Option

class Singleton {
  Singleton._internal();

  factory Singleton() {
    return _instance;
  }

  static final Singleton _instance = Singleton._internal();
}

How to instantiate?

final Singleton object1 = Singleton();
final Singleton object2 = Singleton();

// How to make sure they are the same? You do not need to check they are
// always the same but just to know how to do it.
// using identical Function. 

print(identical(object1, object2)); // true

2. Option

abstract class Singleton {
  Singleton();

  // Named factory constructor returns XTypeSingleton object
 //  if the past value is true, otherwise it returns YTypeSingleton object.
  factory Singleton.instance(bool isX) {
    if (isX) {
      return _xTypeInstance;
    } else {
      return _yTypeInstance;
    }
  }

  static final XTypeSingleton _xTypeInstance = XTypeSingleton.instance;
  static final YTypeSingleton _yTypeInstance = YTypeSingleton.instance;
}

class XTypeSingleton extends Singleton {
  XTypeSingleton._(); // same as XTypeSingleton._internal();

  static final XTypeSingleton instance = XTypeSingleton._();
}

class YTypeSingleton extends Singleton {
  YTypeSingleton._();

  static final YTypeSingleton instance = YTypeSingleton._();
}

How to instantiate?

final Singleton object1 = Singleton.instance(true);
final Singleton object2 = Singleton.instance(true);

final Singleton object3 = Singleton.instance(false);
final Singleton object4 = Singleton.instance(false);

// How to make sure they are the same? You do not need to check they are
// always the same but just to know how to do it.
// using identical Function. 

print(identical(object1, object2)); // true, XTypeSingleton
print(identical(object3, object4)); // true, YTypeSingleton

print(identical(object1, object2)); // false, since XTypeSingleton is not YTypeSingleton

Obviously you can change the if statement in the factory, so you can make a switch statement you can you can make it harder or simplest.

I hope I was cleared and I covered every single detail in this answer. If it's if it was helpful please make it up. Thanks!

Bonus - No need to follow.

I used the last approach on a flatter game just to return color palette based on Brightness

In a game we need more colors rather than using material colors, so we need to return colors with the same name, but different values based on the theme mode.

// @formatter:off

import 'package:flutter/material.dart';

abstract final class Palette {
  const Palette();

  factory Palette.instance(BuildContext context) {
    final Brightness brightness = Theme.of(context).brightness;

    if (brightness == Brightness.light) {
      return _lightPalette;
    } else {
      return _darkPalette;
    }
  }

  static const _LightPalette _lightPalette = _LightPalette();
  static const _DarkPalette _darkPalette = _DarkPalette();

  /// Returns foregroundColor base on [color].
  Color call(Color color) {
    return color.computeLuminance() > 0.5 ? Colors.black : Colors.white;
  }

  Color get red;
  Color get deepRed;
  Color get accentRed;
  Color get green;
  Color get deepGreen;
  Color get accentGreen;
  Color get blue;
  Color get deepBlue;
  Color get accentBlue;
  Color get blueGray;
  Color get gray;
  Color get deepGray;
  Color get ice;
  Color get purple;
  Color get deepPurple;
  Color get deepOrange;
  Color get orange;
  Color get yellow;
  Color get deepYellow;
  Color get pink;
  Color get rose;
  Color get brown;
  Color get deepBrown;
}

final class _LightPalette extends Palette {
  const _LightPalette();

  @override Color get red => const Color(0xFFe43b44);
  @override Color get deepRed => const Color(0xFFa22633);
  @override Color get accentRed => const Color(0xFFff0044);
  @override Color get green => const Color(0xFF3e8948);
  @override Color get deepGreen => const Color(0xFF265c42);
  @override Color get accentGreen => const Color(0xFF63c74d);
  @override Color get blue => const Color(0xFF0099db);
  @override Color get deepBlue => const Color(0xFF124e89);
  @override Color get accentBlue => const Color(0xFF2ce8f5);
  @override Color get blueGray => const Color(0xFF3a4466);
  @override Color get gray => const Color(0xFF8b9bb4);
  @override Color get deepGray => const Color(0xFF5a6988);
  @override Color get ice => const Color(0xFFc0cbdc);
  @override Color get purple => const Color(0xFFb55088);
  @override Color get deepPurple => const Color(0xFF68386c);
  @override Color get deepOrange => const Color(0xFFf77622);
  @override Color get orange => const Color(0xFFfeae34);
  @override Color get yellow => const Color(0xFFfee761);
  @override Color get deepYellow => const Color(0xFFf9c22b);
  @override Color get pink => const Color(0xFFc32454);
  @override Color get rose => const Color(0xFFf04f78);
  @override Color get brown => const Color(0xFFb86f50);
  @override Color get deepBrown => const Color(0xFF733e39);
}

final class _DarkPalette extends Palette {
  const _DarkPalette();

  @override Color get red => const Color(0xFF9B2D33);
  @override Color get deepRed => const Color(0xFF702028);
  @override Color get accentRed => const Color(0xFFAD0733);
  @override Color get green => const Color(0xFF2F6036);
  @override Color get deepGreen => const Color(0xFF204332);
  @override Color get accentGreen => const Color(0xFF478839);
  @override Color get blue => const Color(0xFF076A95);
  @override Color get deepBlue => const Color(0xFF133A60);
  @override Color get accentBlue => const Color(0xFF249EA6);
  @override Color get blueGray => const Color(0xFF2D3349);
  @override Color get gray => const Color(0xFF616C7C);
  @override Color get deepGray => const Color(0xFF424B5F);
  @override Color get ice => const Color(0xFF848B96);
  @override Color get purple => const Color(0xFF7D3B5F);
  @override Color get deepPurple => const Color(0xFF4B2B4D);
  @override Color get deepOrange => const Color(0xFFA8541D);
  @override Color get orange => const Color(0xFFAC7829);
  @override Color get yellow => const Color(0xFFAC9D46);
  @override Color get deepYellow => const Color(0xFFA98523);
  @override Color get pink => const Color(0xFF861E3E);
  @override Color get rose => const Color(0xFFA33A55);
  @override Color get brown => const Color(0xFF7F4F3B);
  @override Color get deepBrown => const Color(0xFF522F2C);
}

How to use this?

extension BuildContextExtension on BuildContext {
  Palette get palette => Palette.instance(this);
}


class Home extends StatelessWidget {
  const Home({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: context.palette.red, // this will return dark red or light red automatically.
    );
  }
}

_LightPalette _DarkPalette

Upvotes: 2

Seth Ladd
Seth Ladd

Reputation: 120439

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
}

Upvotes: 808

Maxim
Maxim

Reputation: 476

For creating singleton object you can use get_it package.

To create singleton:

GetIt.I.registerSingleton(YourClass());

To get singleton object:

GetIt.I<YourClass>()

Upvotes: 0

Hans-J. Schmid
Hans-J. Schmid

Reputation: 353

Easy as that:

class Singleton {
  Singleton._();
  static final _uniqueInstance = Singleton._();
  factory Singleton() => _uniqueInstance;
}

void singleton() {
  final mysingleton = Singleton();
}

Or honoring Smalltalk:

class Singleton {
  Singleton._();
  static final Singleton uniqueInstance = Singleton._();
}

void singleton() {
  final mysingleton = Singleton.uniqueInstance();
}

Upvotes: 2

Guy Luz
Guy Luz

Reputation: 4009

  1. Written cleanly.

  2. The class can't be created more than once (using one instance automatically) which always enforces correct use 😄.

.

class A {
  factory A() {
    return _instance;
  }

  A._singletonContractor(){
    print('Will be called once when object created');
  }
  
  static final A _instance = A._singletonContractor();
}

void main() {
  var a1 = A();
  var a2 = A();
  print(identical(a1, a2));  // true
  print(a1 == a2);           // true
}

Upvotes: 4

Jamirul islam
Jamirul islam

Reputation: 856

In Flutter, a singleton class is a class that allows you to create only one instance throughout the lifetime of your application. Singletons are often used to manage global state or to ensure that there is only one instance of a particular class. You can implement a singleton in Flutter using the following code pattern -

class MySingleton {
  // Private constructor
  MySingleton._privateConstructor();

  // The single instance of the class
  static final MySingleton _instance = MySingleton._privateConstructor();

  // Factory constructor to provide access to the instance
  factory MySingleton() {
    return _instance;
  }

  // Add your methods and properties here
}




void main() {
  MySingleton instance1 = MySingleton();
  MySingleton instance2 = MySingleton();

  print(identical(instance1, instance2)); // This will print 'true' since it's the same instance.
}

Upvotes: -1

Ivan Yoed
Ivan Yoed

Reputation: 4395

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();

  late String _valueToBeSet;
  late String _valueAlreadyInSingleton;
  late 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);

}

Upvotes: 15

Hamed Rezaee
Hamed Rezaee

Reputation: 7222

Here is a simple answer:

  • Class should have a private and static property of its type.
  • The constructor should be private to prevent external object initialization.
  • Check if the instance is null, if yes create an instance and return it, otherwise return the existing instance.

Implementation (Factory Contractor)

class Singleton {
  static Singleton? _instance;

  Singleton._();

  factory Singleton() => _instance ??= Singleton._();

  void someMethod() {
    // ...
  }
}

Usage

Singleton().someMethod();

// prints "Same Singleton instance? true"
print('Same Singleton instance? ${Singleton().hashCode == Singleton().hashCode}');

Implementation (Lazy Loading)

class Singleton {
  static Singleton? _instance;

  Singleton._();

  static Singleton get instance => _instance ??= Singleton._();

  void someMethod() {
    ...
  }

  ...
}

Implementation (Eager Loading)

class Singleton {
  static Singleton _instance = Singleton._();

  Singleton._();

  static Singleton get instance => _instance;

  void someMethod() {
    ...
  }

  ...
}

Usage

Singleton.instance.someMethod();

Upvotes: 88

Christian Findlay
Christian Findlay

Reputation: 7670

There is nothing tricky about creating a Singleton in Dart. You can declare any variable in a top-level (global) location, which is a Singleton by default. You can also declare a variable as a static member of a class. This is a singleton A.

class A {}

final a = A();

However, the above does not allow you to replace the instance for testing. The other issue is that as the app grows in complexity, you may want to convert global or static variables to transient dependencies inside your classes. If you use dependency injection, you can change a dependency inside your composition at any time. This is an example of using ioc_container to configure a singleton instance of A in the root of an app. You can change this to a transient dependency any time by using add instead of addSingletonService

import 'package:ioc_container/ioc_container.dart';

class A {}

void main(List<String> arguments) {
  final builder = IocContainerBuilder()..addSingletonService(A());
  final container = builder.toContainer();
  final a1 = container<A>();
  final a2 = container<A>();
  print(identical(a1, a2));
}

The above prints true because the app will only ever mint one instance of A.

Upvotes: 1

BIS Tech
BIS Tech

Reputation: 19414

Create Singleton

class PermissionSettingService {
  static PermissionSettingService _singleton = PermissionSettingService._internal();

  factory PermissionSettingService() {
    return _singleton;
  }

  PermissionSettingService._internal();
}

Reset Singleton

// add this function inside the function
  void reset() {
   _singleton = PermissionSettingService._internal();
  }

Upvotes: 1

Javeed Ishaq
Javeed Ishaq

Reputation: 7105

how to create a singleton instance of a class in dart flutter

  class ContactBook {
      ContactBook._sharedInstance();
      static final ContactBook _shared = ContactBook._sharedInstance();
      factory ContactBook() => _shared;
    }

Upvotes: 5

cattarantadoughan
cattarantadoughan

Reputation: 509

This is my way of doing singleton which accepts parameters (you can paste this directly on https://dartpad.dev/ ):

void main() {
  
  Logger x = Logger('asd');
  Logger y = Logger('xyz');
  
  x.display('Hello');
  y.display('Hello There');
}


class Logger{
  
  
  Logger._(this.message);
  final String message;
  static Logger _instance = Logger._('??!?*');
  factory Logger(String message){
    if(_instance.message=='??!?*'){
      _instance = Logger._(message);
    }
    return _instance;
  }
  
  void display(String prefix){
    print(prefix+' '+message);
  }
  
}

Which inputs:

Hello asd
Hello There asd

The '??!?*' you see is just a workaround I made to initialize the _instance variable temporarily without making it a Logger? type (null safety).

Upvotes: 4

Jitesh Mohite
Jitesh Mohite

Reputation: 34160

Singleton objects can be betterly created with null safety operator and factory constructor.

class Singleton {
  static Singleton? _instance;

  Singleton._internal();

  factory Singleton() => _instance ??= Singleton._internal();
  
  void someMethod() {
    print("someMethod Called");
  }
}

Usage:

void main() {
  Singleton object = Singleton();
  object.someMethod(); /// Output: someMethod Called
}

Note: ?? is a Null aware operator, it returns the right-side value if the left-side value is null, which means in our example _instance ?? Singleton._internal();, Singleton._internal() will be return first time when object gets called , rest _instance will be return.

Upvotes: 6

Maxim Saplin
Maxim Saplin

Reputation: 4652

If you happen to be using Flutter and provider package for state management, creating and using a singleton is quite straightforward.

  1. Create an instance
  void main() {
      runApp(
        MultiProvider(
          providers: [
            ChangeNotifierProvider(create: (context) => SomeModel()),
            Provider(create: (context) => SomeClassToBeUsedAsSingleton()),
          ],
          child: MyApp(),
        ),
      );
    }
  1. Get the instance
Widget build(BuildContext context) { 
  var instance = Provider.of<SomeClassToBeUsedAsSingleton>(context); 
  ...

Upvotes: 8

Aman Ansari
Aman Ansari

Reputation: 139

** 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!;
  }
}

Upvotes: 5

Salvatore Gerace
Salvatore Gerace

Reputation: 5

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.

Upvotes: -4

thisisyusub
thisisyusub

Reputation: 707

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.

Upvotes: 8

George Yacoub
George Yacoub

Reputation: 1456

I use this simple pattern on dart and previously on Swift. I like that it's terse and only one way of using it.

class Singleton {
  static Singleton shared = Singleton._init();
  Singleton._init() {
    // init work here
  }

  void doSomething() {
  }
}

Singleton.shared.doSomething();

Upvotes: 1

Suragch
Suragch

Reputation: 511528

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.

Upvotes: 604

sultanmyrza
sultanmyrza

Reputation: 5392

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);

Upvotes: 9

Ticore Shih
Ticore Shih

Reputation: 375

Dart singleton by const constructor & factory

class Singleton {
  factory Singleton() =>
    Singleton._internal_();
  Singleton._internal_();
}
 
 
void main() {
  print(new Singleton() == new Singleton());
  print(identical(new Singleton() , new Singleton()));
}

Upvotes: 14

Lucas Breitembach
Lucas Breitembach

Reputation: 1683

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}

Upvotes: 16

daveoncode
daveoncode

Reputation: 19578

After reading all the alternatives I came up with this, which reminds me a "classic singleton":

class AccountService {
  static final _instance = AccountService._internal();

  AccountService._internal();

  static AccountService getInstance() {
    return _instance;
  }
}

Upvotes: 11

Filip Jerga
Filip Jerga

Reputation: 75

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;
  }
}

Upvotes: 3

Greg Lowe
Greg Lowe

Reputation: 16261

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.

Upvotes: 51

DazChong
DazChong

Reputation: 3552

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';

Upvotes: 8

sprestel
sprestel

Reputation: 66

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));

Upvotes: 3

Vilsad P P
Vilsad P P

Reputation: 1559

This should work.

class GlobalStore {
    static GlobalStore _instance;
    static GlobalStore get instance {
       if(_instance == null)
           _instance = new GlobalStore()._();
       return _instance;
    }

    _(){

    }
    factory GlobalStore()=> instance;


}

Upvotes: 3

Jacob Phillips
Jacob Phillips

Reputation: 9254

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
}

Upvotes: 9

Related Questions