First of all why to create a Singleton object? The answer to this question is very simple, If you have a class or object that you use twice and you want to repeat using the same instance of it, you guest use a Singleton.
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.
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
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.
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
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
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.
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
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!
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.
);
}
}