Initialize class with asynchronous variable - dart

How can I initialize a class with asynchronous variables so that they are set before the class is used? I have my class currently just calling an async init function but I would have to call that separately to wait for it to finish:
class Storage {
String imageDirectory;
String jsonDirectory;
SharedPreferences instance;
String uuid;
init() async {
imageDirectory = '${(await getApplicationDocumentsDirectory()).path}/image_cache/';
jsonDirectory = '${(await getApplicationDocumentsDirectory()).path}/json_cache/';
instance = await SharedPreferences.getInstance();
uuid = instance.getString("UUID");
}
}
Is there a better way to do this?

You might hope that you could have async factory constructors, but they aren't allowed.
So one solution is a static getInstance(), for example:
class Storage {
static Future<Storage> getInstance() async {
String docsFolder = (await getApplicationDocumentsDirectory()).path;
return new Storage(
docsFolder + '/image_cache/',
docsFolder + '/json_cache/',
(await SharedPreferences.getInstance()).getString('UUID'));
}
String imageDirectory;
String jsonDirectory;
String uuid;
Storage(this.imageDirectory, this.jsonDirectory, this.uuid);
}
You could pass parameters into getInstance and thus into the constructor, as required. Call the above with:
Storage s = await Storage.getInstance();

Related

how to make singleton class with some initialization code?

I have tried the answers in here How do you build a Singleton in Dart?
but I can't achieve what I want. so basically I want to make a Shared Preference Service as a singleton class. currently my code is like this. this is just a regular class, not a singleton.
class SharedPreferenceService {
late SharedPreferences _prefs;
SharedPreferenceService() {
SharedPreferences.getInstance().then((value) => _prefs = value);
}
Future<void> setIntroPagesHaveBeenViewed() async {
await _prefs.setBool(SharedPreferenceKey.INTRODUCTION_PAGES_HAVE_BEEN_VIEWED, true);
}
Future<bool> checkIfIntroPagesHaveBeenViewed() async {
return _prefs.getBool(SharedPreferenceKey.INTRODUCTION_PAGES_HAVE_BEEN_VIEWED) ?? false;
}
}
I need a singleton class, but when the instance is initialize for the first time, I also need to initialize _pref , so then I can access that _pref on the methods
Your problem is that initialization is asynchronous.
That means that the first time the singleton instance is accessed, that access needs to be asynchronous too (and so does any further access which happens before the initialization completes). However, the usage pattern of a singleton like this is such that you don't know which access is the first. So you have to make every access asynchronous.
Example:
class SharedPreferenceService {
static final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
Future<void> setIntroPagesHaveBeenViewed() async {
await (await _prefs).setBool(
SharedPreferenceKey.INTRODUCTION_PAGES_HAVE_BEEN_VIEWED, true);
}
Future<bool> checkIfIntroPagesHaveBeenViewed() async {
return (await _prefs).getBool(
SharedPreferenceKey.INTRODUCTION_PAGES_HAVE_BEEN_VIEWED) ?? false;
}
}
If all the methods are asynchronous anyway, that extra delay is not going to be a problem.
If you really, really only want to do that extra await if absolutely necessary,
you can cache the value, like you try to here:
class SharedPreferenceService {
static final Future<SharedPreferences> _prefsFuture = SharedPreferences.getInstance();
static SharedPreferences? _prefs;
Future<void> setIntroPagesHaveBeenViewed() async {
var prefs = _prefs ??= await _prefsFuture;
await _prefs.setBool(
SharedPreferenceKey.INTRODUCTION_PAGES_HAVE_BEEN_VIEWED, true);
}
Future<bool> checkIfIntroPagesHaveBeenViewed() async {
var prefs = _prefs ??= await _prefsFuture;
return _prefs.getBool(
SharedPreferenceKey.INTRODUCTION_PAGES_HAVE_BEEN_VIEWED) ?? false;
}
}

Constructor with not initialized final class variables

I have this class:
class Entry {
final String id;
final List<ListEntry> listEntries;
Entry({this.listEntries}):
id = Uuid().v4();
Entry.withId({this.id, this.listEntries});
}
// create new class instance
var e = Entry();
Now when I call any method on e.listEntries I will get a NPE because it is not initialized. Is there a way to have it default to an empty list in case the constructor argument is not provided?
You can use a Factory constructor:
class Entry {
final String id;
final List<String> listEntries;
factory Entry({List<String> listEntries}) {
return Entry._(listEntries ?? []);
}
Entry._(this.listEntries):
id = Uuid().v4();
Entry.withId({this.id, this.listEntries});
}
You can initialize the field in the initializer list instead of using this.ListEntries (an initializing formal).
class Entry {
final String id;
final List<ListEntry> listEntries;
Entry({List<ListEntry> listEntries})
: this.withId(id: Uuid().v4(), listEntries: listEntries);
Entry.withId({this.id, this.listEntries})
: listEntries = listEntries ?? [];
}
// create new class instance
var e = Entry();
Here I reuse the Entry.withId constructor so I only have to write things once.

dart await on constructor

What pattern should I use in this example to load and process some data. As value returns a value, it's not acceptable to have d as a Future. How can I get the constructor to wait until load has completed before continuing?
void main() {
var data = new Data(); // load data
print(data.value()); // data.d is still null
}
class Data {
String d;
Data() {
load();
}
Future<void> load() async {
d = await fn(); // some expensive function (e.g. loading a database)
}
String value() {
return d;
}
}
You cannot make a constructor asynchronous.
An asynchronous function needs to return a Future, and a constructor needs to return an instance of the class itself. Unless the class is a future, the constructor cannot be asynchronous (and even then, it's not really the same thing, and you can't use async/await).
So, if your class needs asynchronous set-up, you should provide the user with a static factory method instead of a constructor. I'd usually hide the constructor then.
class Data {
String _d;
Data._();
static Future<Data> create() async {
var data = Data._();
await data._load();
return data;
}
Future<void> _load() async {
_d = await fn();
}
String get value => _d;
}
As an alternative design, I wouldn't even put the load method on the class, just do the operation in the static factory method:
class Data {
String _d;
Data._(this._d);
static Future<Data> create() async => Data._(await fn());
String get value => _d;
}
Obviously other constraints might require that load has access to the object.

Dependency injection resolution of async factory

Having some issues with injecting a database connection into a class via the di.dart package. Specifically, resolving an async dependency in the toFactory option.
Both attempts result in the
NoSuchMethodError: Class '_Future' has no instance method 'query'.
error and it's unclear the correct path forward. I would prefer to keep the conn property without being wrapped in a Future. I've attempted doing the unwrapping of the Future in the class constructor but async constructors are not allowed in Dart at this time.
import 'package:postgresql/pool.dart';
import 'package:postgresql/postgresql.dart';
import 'package:di/di.dart';
main() async {
var uri = "postgres://username:password#localhost:5432/database";
var pool = new Pool(uri, minConnections: 2, maxConnections: 5);
await pool.start();
Module module = new Module();
module.bind(TestQuery);
module.bind(TestController);
module.bind(Pool, toValue: pool);
module.bind(Connection, toFactory: (pool) => pool.connect(), inject: [Pool]);
var injector = new ModuleInjector([module]);
var html = await injector.get(TestController).index();
print(html);
}
class BaseQuery {
Connection conn;
BaseQuery(Connection this.conn);
}
class TestQuery extends BaseQuery {
TestQuery(Connection conn) : super(conn); // type '_Future' is not a subtype of type 'Connection' of 'conn' where
run() async {
var results = await conn.query("select 1").toList();
// Do some data manipulation
return results;
}
}
class TestController {
TestQuery testQuery;
TestController(TestQuery this.testQuery);
index() async {
var results = await testQuery.run();
var html = "<pre>" + results.toString() + "</pre>";
return html;
}
}
I can reproduce but when I change the line to
var conn = await injector.get(Connection);
var res = await conn.query("select 1").toList();
it working fine.
I think your code should work as well though.

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?
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.
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 (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();
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 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}
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()));
}
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);
}
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;
}
}
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
}
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);
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';
If you happen to be using Flutter and provider package for state management, creating and using a singleton is quite straightforward.
Create an instance
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => SomeModel()),
Provider(create: (context) => SomeClassToBeUsedAsSingleton()),
],
child: MyApp(),
),
);
}
Get the instance
Widget build(BuildContext context) {
var instance = Provider.of<SomeClassToBeUsedAsSingleton>(context);
...
** 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!;
}
}
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.
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;
}
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).
This should work.
class GlobalStore {
static GlobalStore _instance;
static GlobalStore get instance {
if(_instance == null)
_instance = new GlobalStore()._();
return _instance;
}
_(){
}
factory GlobalStore()=> 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));
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;
}
}
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();
This is also a way to create a Singleton class
class Singleton{
Singleton._();
static final Singleton db = Singleton._();
}
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();
}
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.
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.

Resources