pass initial data into a singleton factory in dart - dart

I would like to pass some initial information into a singleton in dart.
Unfortunately, the information I like to access is null (see dartpad output below)
It seems like I get a new instance of my object and not the singleton but I can not wrap my head around it. Any idea?
ElmCommandProvider.fromMetaData
ElmCommandProvider._internal()
ElmCommandProvider._init
ElmCommandProvider()
null
This is the code which can be pasted in DartPad
class Command {
Command(this.i);
final int i;
}
class MetaData {
MetaData(this.i);
final int i;
}
class ElmCommandProvider {
List<Command> commandsList;
bool _lock = false;
static Map<String, MetaData> _metaDataPool;
factory ElmCommandProvider.fromMetaData(Map<String, MetaData> metaDataPool) {
print('ElmCommandProvider.fromMetaData');
assert(!_singleton._lock, "it's a singleton that can't re-defined");
ElmCommandProvider._metaDataPool = metaDataPool;
_singleton._lock = true;
ElmCommandProvider._init();
return _singleton;
}
factory ElmCommandProvider() {
print('ElmCommandProvider()');
return _singleton;
}
static final ElmCommandProvider _singleton =
new ElmCommandProvider._internal();
ElmCommandProvider._internal() {
print('ElmCommandProvider._internal()');
}
ElmCommandProvider._init() {
print('ElmCommandProvider._init');
commandsList =
_metaDataPool.values.map((bloc) => Command(bloc.i)).toList();
}
}
void main() {
ElmCommandProvider.fromMetaData({'1': MetaData(1), '2': MetaData(2)});
print( ElmCommandProvider().commandsList);
}

_init() should not be a constructor. Or at least there is no need for it to be one and it's confusing you. It should be changed to a static method or a private instance method.
When you do commandsList= in ElmCommandProvider._init(), commandsList is referring to the commandsList instance variable in the new ElmCommandProvider object you're creating with the constructor. You likely actually mean to modify the singleton's commandsList so you should have been doing singleton.commandsList = instead of just commandsList =.
Example working code with static method:
class Command {
Command(this.i);
final int i;
}
class MetaData {
MetaData(this.i);
final int i;
}
class ElmCommandProvider {
List<Command> commandsList;
bool _lock = false;
static Map<String, MetaData> _metaDataPool;
factory ElmCommandProvider.fromMetaData(Map<String, MetaData> metaDataPool) {
print('ElmCommandProvider.fromMetaData');
assert(!_singleton._lock, "it's a singleton that can't re-defined");
ElmCommandProvider._metaDataPool = metaDataPool;
_singleton._lock = true;
_init();
return _singleton;
}
factory ElmCommandProvider() {
print('ElmCommandProvider()');
return _singleton;
}
static final ElmCommandProvider _singleton =
new ElmCommandProvider._internal();
ElmCommandProvider._internal() {
print('ElmCommandProvider._internal()');
}
static _init() {
print('ElmCommandProvider._init');
_singleton.commandsList =
_metaDataPool.values.map((bloc) => Command(bloc.i)).toList();
}
}
void main() {
ElmCommandProvider.fromMetaData({'1': MetaData(1), '2': MetaData(2)});
print( ElmCommandProvider().commandsList);
}
Example working code with private instance method:
class Command {
Command(this.i);
final int i;
}
class MetaData {
MetaData(this.i);
final int i;
}
class ElmCommandProvider {
List<Command> commandsList;
bool _lock = false;
static Map<String, MetaData> _metaDataPool;
factory ElmCommandProvider.fromMetaData(Map<String, MetaData> metaDataPool) {
print('ElmCommandProvider.fromMetaData');
assert(!_singleton._lock, "it's a singleton that can't re-defined");
ElmCommandProvider._metaDataPool = metaDataPool;
_singleton._lock = true;
_singleton._init();
return _singleton;
}
factory ElmCommandProvider() {
print('ElmCommandProvider()');
return _singleton;
}
static final ElmCommandProvider _singleton =
new ElmCommandProvider._internal();
ElmCommandProvider._internal() {
print('ElmCommandProvider._internal()');
}
void _init() {
print('ElmCommandProvider._init');
commandsList =
_metaDataPool.values.map((bloc) => Command(bloc.i)).toList();
}
}
void main() {
ElmCommandProvider.fromMetaData({'1': MetaData(1), '2': MetaData(2)});
print( ElmCommandProvider().commandsList);
}

Related

How to define a factory method in dart abstracts

I want to extend or implement some BaseModel so that I can summarize some hard coded methods exist in a lot of my classes.
So I want to declare some methods and factory methods as static and required methods in all the inherited classes.
The following is a draft of what I tried.
How can I do that properly?
#immutable
abstract class BaseDataModel {
// BaseDataModel();
}
abstract class DataModel{
DataModel();
factory DataModel.fromJson(Map json) => ConcreteDataModel();
Map<String, dynamic> toJson() => {};
}
class ConcreteDataModel extends DataModel{
}
// extension DataModelJsonHandler on List<DataModel> {
// List<DataModel> itemsFromJson(String json) {
// List<DataModel> modelItems = [];
// List jsonlist = jsonDecodeSafeToList(json);
// jsonlist.forEach((item) {
// modelItems.add(DataModel.fromJson(item));
// });
// return modelItems;
// }
// String itemsToJson() {
// final List jsonlist = [];
// forEach((item) {
// jsonlist.add(item.toJson());
// });
// return jsonEncodeSafe(jsonlist);
// }
// }
class DataModelJsonHandler {
static List<DataModel> itemsFromJson(String json) {
List<DataModel> items = [];
List jsonlist = jsonDecodeSafeToList(json);
jsonlist.forEach((item) {
items.add(DataModel.fromJson(item));
});
return items;
}
static String itemsToJson(List<DataModel> items) {
final List jsonlist = [];
items.forEach((item) {
jsonlist.add(item.toJson());
});
return jsonEncodeSafe(jsonlist);
}
}
class User implements DataModel {
final String? name;
User({this.name});
#override
Map<String, dynamic> toJson() {
final Map<String, dynamic> map = <String, dynamic>{};
map['name'] = name;
return map;
}
}
void main() {
List<User> users = [User(name: 'User 1'), User(name: 'User 2')];
String usersJson = DataModelJsonHandler.itemsToJson();
log(usersJson);
List<User> usersFromJson = DataModelJsonHandler.itemsFromJson(usersJson); // Error
}

Dart Null Safety giving errors for typedef Function

I have an example that works in dart before null safety that doesn't after upgrading to Null safety. I can't figure out what's going on.
It gives me the error
A value of type 'dynamic Function()?' can't be returned from the method 'doSomething' because it has a return type of 'dynamic Function()'
But I didn't define anything as Nullable.
typedef RandomFunction = Function();
class RegisteredFunctions {
Map<String, RandomFunction> types = {};
static final RegisteredFunctions _registry = RegisteredFunctions._init();
RegisteredFunctions._init() {}
factory RegisteredFunctions() {
return _registry;
}
void registerFunction(String name, RandomFunction func) {
types[name] = func;
}
RandomFunction doSomething(String id) => types[id]; //<---- gives error
}
void doStuff(){
print('Doing Something');
}
void main() {
RegisteredFunctions functions = RegisteredFunctions();
functions.registerFunction('func1', doStuff);
functions.doSomething('func1')();
}
For anyone else trying to figure it out.. this fixed it.
typedef RandomFunction = Object? Function();
class RegisteredFunctions {
Map<String, RandomFunction> types = {};
static final RegisteredFunctions _registry = RegisteredFunctions._init();
RegisteredFunctions._init() {}
factory RegisteredFunctions() {
return _registry;
}
void registerFunction(String name, RandomFunction func) {
types[name] = func;
}
// RandomFunction doSomething(String id) => types[id]; // <----- Doesn't work
// RandomFunction doSomething(String id) => types[id]!; // <----- Works
RandomFunction doSomething(String id) { // <----- Works better
RandomFunction? func = types[id];
if (func != null) {
return func;
} else {
return (){};
}
}
}
void doStuff(){
print('Doing Something');
}
void doOtherStuff(){
print('Doing Something else');
}
void main() {
RegisteredFunctions functions = RegisteredFunctions();
functions.registerFunction('func1', doStuff);
functions.registerFunction('func2', doOtherStuff);
functions.doSomething('func1')();
functions.doSomething('func2')();
}

What is an equivalent for Dart 2 to `typeof` of TypeScript?

I'm new to Dart 2. I want a class to have a property. It's a reference of other class. it's not an instance but class itself. In TypeScript, it's possible to write as below. Is there a same way in Dart 2?
class Item { }
class ItemList {
itemClass: typeof Item;
}
const itemList = new ItemList();
itemList.itemClass = Item;
UPDATED:
I added some more context. The following is minimal sample code. I want to delegate a role of instantiation to super class.
class RecordBase {
id = Math.random();
toJson() {
return { "id": this.id };
};
}
class DbBase {
recordClass: typeof RecordBase;
create() {
const record = new this.recordClass();
const json = record.toJson();
console.log(json);
}
}
class CategoryRecord extends RecordBase {
toJson() {
return { "category": "xxxx", ...super.toJson() };
};
}
class TagRecord extends RecordBase {
toJson() {
return { "tag": "yyyy", ...super.toJson() };
};
}
class CategoryDb extends DbBase {
recordClass = CategoryRecord;
}
class TagDb extends DbBase {
recordClass = TagRecord;
}
const categoryDb = new CategoryDb();
categoryDb.create();
const tagDb = new TagDb();
tagDb.create();
I have tried to make you sample code into Dart. As I told before, you cannot get a reference to a class and call the constructor on runtime based on this reference.
But you can make a reference to a method which constructs the object of you class.
import 'dart:math';
class RecordBase {
static final Random _rnd = Random();
final int id = _rnd.nextInt(100000);
Map<String, dynamic> toJson() => <String, dynamic>{'id': id};
}
abstract class DbBase {
final RecordBase Function() getRecordClass;
RecordBase record;
Map<String, dynamic> json;
DbBase(this.getRecordClass);
void create() {
record = getRecordClass();
json = record.toJson();
print(json);
}
}
class CategoryRecord extends RecordBase {
#override
Map<String, dynamic> toJson() {
return <String, dynamic>{'category': 'xxxx', ...super.toJson()};
}
}
class TagRecord extends RecordBase {
#override
Map<String, dynamic> toJson() {
return <String, dynamic>{'tag': 'yyyy', ...super.toJson()};
}
}
class CategoryDb extends DbBase {
CategoryDb() : super(() => CategoryRecord());
}
class TagDb extends DbBase {
TagDb() : super(() => TagRecord());
}
void main() {
final categoryDb = CategoryDb();
categoryDb.create(); // {category: xxxx, id: 42369}
final tagDb = TagDb();
tagDb.create(); // {tag: yyyy, id: 97809}
}
I am not really sure if the create() method should be seen as a method or a constructor. So I choose to make it a method to be closer to your code.

Air iOS SharedObject deleted after updating

I'm using SharedObjects in my game to store the progress of the player in the game (level scores, unlocked levels, etc.).
Everything is working fine, but the problem is when I submitted an update of the game (with the same certificates and the same names of the .swf and .ipa files) when the game is updated the old SharedObject is deleted and this is very big problem for the game and for me.
Both versions of the game are made with Flash CS 6 and Air SDK 3.5.
Any idea why the SharedObject is deleted, how can I prevent that?
I'm assuming that the reason why SharedObject becomes overwritten is because it's bundled with the .ipa during conversion.
I understand that will not help your current situation with salvaging your SharedObject but you could try using flash.filesystem to read/write your data to a preference file instead of employing SharedObject in the future.
Below is my Archive class that I use with my own work, but I've never developed for iOS before so i'm not certain that it will function as it does on other deployment targets, although I believe it should.
Use Case:
//Imports
import com.mattie.data.Archive;
import com.mattie.events.ArchiveEvent;
//Constants
private static const PREF_CANVAS_VOLUME:String = "prefCanvasVolume";
private static const DEFAULT_VOLUME:Number = 0.5;
//Initialize Archive
private function initArchive():void
{
archive = new Archive();
archive.addEventListener(ArchiveEvent.LOAD, init);
archive.load();
}
//Initialize
private function init(event:ArchiveEvent):void
{
archive.removeEventListener(ArchiveEvent.LOAD, init);
canvasVolume = archive.read(PREF_CANVAS_VOLUME, DEFAULT_VOLUME);
}
//Application Exiting Event Handler
private function applicationExitingEventHandler(event:Event):void
{
stage.nativeWindow.removeEventListener(Event.CLOSING, applicationExitingEventHandler);
archive.write(PREF_CANVAS_VOLUME, canvas.volume);
archive.addEventListener(ArchiveEvent.SAVE, archiveSavedEventHandler);
archive.save();
}
//Archive Saved Event Handler
private function archiveSavedEventHandler(event:ArchiveEvent):void
{
archive.removeEventListener(ArchiveEvent.SAVE, archiveSavedEventHandler);
NativeApplication.nativeApplication.exit();
}
Archive Class
package com.mattie.data
{
//Imports
import com.mattie.events.ArchiveEvent;
import flash.data.EncryptedLocalStore;
import flash.desktop.NativeApplication;
import flash.events.EventDispatcher;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.net.registerClassAlias;
import flash.utils.ByteArray;
//Class
public final class Archive extends EventDispatcher
{
//Properties
private static var singleton:Archive;
//Variables
private var file:File;
private var data:Object;
//Constructor
public function Archive()
{
if (singleton)
{
throw new Error("Archive is a singleton that is only accessible via the \"archive\" public property.");
}
file = File.applicationStorageDirectory.resolvePath(NativeApplication.nativeApplication.applicationID + "Archive");
data = new Object();
registerClassAlias("Item", Item);
}
//Load
public function load():void
{
if (file.exists)
{
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.READ);
data = fileStream.readObject();
fileStream.close();
}
dispatchEvent(new ArchiveEvent(ArchiveEvent.LOAD));
}
//Read
public function read(key:String, defaultValue:* = null):*
{
var value:* = defaultValue;
if (data[key] != undefined)
{
var item:Item = Item(data[key]);
if (item.encrypted)
{
var bytes:ByteArray = EncryptedLocalStore.getItem(key);
if (bytes == null)
{
return value;
}
switch (item.value)
{
case "Boolean": value = bytes.readBoolean(); break;
case "int": value = bytes.readInt(); break;
case "uint": value = bytes.readUnsignedInt(); break;
case "Number": value = bytes.readDouble(); break;
case "ByteArray": bytes.readBytes(value = new ByteArray()); break;
default: value = bytes.readUTFBytes(bytes.length);
}
}
else
{
value = item.value;
}
}
return value;
}
//Write
public function write(key:String, value:*, encrypted:Boolean = false, autoSave:Boolean = false):void
{
var oldValue:* = read(key);
if (oldValue != value)
{
var item:Item = new Item();
item.encrypted = encrypted;
if (encrypted)
{
var constructorString:String = String(value.constructor);
constructorString = constructorString.substring(constructorString.lastIndexOf(" ") + 1, constructorString.length - 1);
item.value = constructorString;
var bytes:ByteArray = new ByteArray();
switch (value.constructor)
{
case Boolean: bytes.writeBoolean(value); break;
case int: bytes.writeInt(value); break;
case uint: bytes.writeUnsignedInt(value); break;
case Number: bytes.writeDouble(value); break;
case ByteArray: bytes.writeBytes(value); break;
default: bytes.writeUTFBytes(value);
}
EncryptedLocalStore.setItem(key, bytes);
}
else
{
item.value = value;
}
data[key] = item;
dispatchEvent(new ArchiveEvent(ArchiveEvent.WRITE, key, oldValue, value));
if (autoSave)
{
save();
}
}
}
//Remove
public function remove(key:String, autoSave:Boolean = false):void
{
if (data[key] != undefined)
{
var oldValue:* = read(key);
if (Item(data[key]).encrypted)
{
EncryptedLocalStore.removeItem(key);
}
delete data[key];
dispatchEvent(new ArchiveEvent(ArchiveEvent.DELETE, key, oldValue));
if (autoSave)
{
save();
}
}
}
//Contains
public function contains(key:String):Boolean
{
return (data[key] != undefined);
}
//Save
public function save():void
{
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.WRITE);
fileStream.writeObject(data);
fileStream.close();
dispatchEvent(new ArchiveEvent(ArchiveEvent.SAVE));
}
//Get Singleton
public static function get archive():Archive
{
if (!singleton)
{
singleton = new Archive();
}
return singleton;
}
}
}
//Item
class Item
{
//Variables
public var value:*;
public var encrypted:Boolean = false;
}
Archive Event Class
package com.mattie.events
{
//Imports
import flash.events.Event;
//Class
public class ArchiveEvent extends Event
{
//Constants
public static const LOAD:String = "load";
public static const WRITE:String = "write";
public static const DELETE:String = "delete";
public static const SAVE:String = "save";
//Properties
public var key:String;
public var oldValue:*;
public var newValue:*;
//Constructor
public function ArchiveEvent(type:String, key:String = null, oldValue:* = null, newValue:* = null)
{
super(type);
this.key = key;
this.oldValue = oldValue;
this.newValue = newValue;
}
//Clone
public override function clone():Event
{
return new ArchiveEvent(type, key, oldValue, newValue);
}
//To String
public override function toString():String
{
return formatToString("ArchiveEvent", "type", "key", "oldValue", "newValue");
}
}
}

Other Classes in Class arguments

How can I set another Class as a Class constructor argument?
class MyClass {
String message = 'myClass';
void print() {
print(this.message);
}
}
class TestClass {
var myClass;
TestClass(???) {
this.myClass = ???(); // ???
}
void debug() {
print('DEBUG: ');
this.myClass.print();
}
}
main() {
MyClass myClass;
testClass(myClass);
}
You can't (at least not now). Your best option is to pass a builder function into the constructor.
class MyClass {
var clazzBuilder;
MyClass(this.clazzBuilder);
doStuff() {
var instance = clazzBuilder();
:
}
}
and then use it like this
var myTest = new MyClass(() => new Test());
var myProd = new MyClass(() => new Prod());
Note that the Dart team is planning to add both class concept (.type) and reflection to the language.
What do you want actually do? Often people try to apply paradigms from some languages in some other languages which isn't the best way to do some things.
Since print is an instance method and not a static method, you don't need to pass a class itself to TestClass, just an instance of it. Your program can be fixed like so:
class MyClass {
String message = 'myClass';
void print() {
print(this.message);
}
}
class TestClass {
var myClass;
TestClass(MyClass myClass) {
this.myClass = myClass;
}
void debug() {
print('DEBUG: ');
this.myClass.print();
}
}
main() {
MyClass myClass = new MyClass();
testClass(myClass);
}
Or, more idiomatically:
class MyClass {
String message = 'myClass';
void print() => print(message);
}
class TestClass {
var myClass;
TestClass(this.myClass);
void debug() {
print('DEBUG: ');
myClass.print();
}
}
main() {
testClass(new MyClass());
}

Resources