I have a database service (and others) that I want to use with Get_It. But before I can call the methods in the database service, I have to make sure that it's initialized.
So, for a normal service, I would do this:
sl.registerLazySingleton<DatabaseService>(() => DatabaseService());
and then later when I needed it, I would do this:
var db = sl<DatabaseService>();
But because this database service has to be initialized before I can use it, and because it requires an async/await during that process, I have to create an init
method like this:
Future<Database> initDb() async {
// Sqflite.devSetDebugModeOn(true);
String dbPath = await getDatabasesPath();
String documentPath = join(dbPath, sqlDatabaseName);
return await openDatabase(documentPath, version: 1, onOpen: (db) async {
log.config('database found, opening it...');
}, onCreate: (Database db, int version) async {
log.config('database not found, created it...');
createTableCommands.definitions.forEach((table, command) async {
await db
.execute(command)
.then((_) => log.config('Creating $table table'))
.catchError((e) => log.shout('Error creating $table table: $e'));
});
});
}
Then I create a getter like this:
Future<Database> get database async {
if (_database != null) return _database;
_database = await initDb();
return _database;
}
and everywhere in the code that I want to use it, I do it like this:
Future<void> createTable(String tableName) async {
final db = await database;
var command = createTableCommands.definitions[tableName];
await db
.execute(command)
.then((_) => log.config('Creating $tableName table'))
.catchError((e) => log.shout('Error creating $tableName table: $e'));
}
And that works.
But it seems really contrived to put a request to the getter at the top of every method that needs the resource. Maybe that's the Dart way, I'm new to this.
Anyway, I saw another approach, which was to make an un-awaited call to the initializer in the constructor and then it's up to the calling code to make sure that the initialization is finished before proceeeding.
I found that here: https://stackoverflow.com/questions/38933801/calling-an-async-method-from-component-constructor-in-dart
class MyComponent{
MyComponent();
Future init() async {
print("init");
}
}
void main() async {
var c = new MyComponent();
await c.init();
print("done");
}
or another approach here...
class MyComponent{
Future _doneFuture;
MyComponent() {
_doneFuture = _init();
}
Future _init() async {
print("init");
}
Future get initializationDone => _doneFuture
}
void main() async {
var c = new MyComponent();
await c.initializationDone;
print("done");
}
I tried using the last one, but what gets registered with Get_It is a Future
, so when I reference it, I get a future instead of an instance.
So I found another pattern, here: https://stackoverflow.com/questions/54549235/dart-await-on-constructor
In this one, it seems they want to use a factory method for creating the instance.
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;
}
or
class Data {
String _d;
Data._(this._d);
static Future<Data> create() async => Data._(await fn());
String get value => _d;
}
My question is this: Is there a recommended pattern for handling this kind of situation? I can't be the only one wondering about it. For the moment, I'm going with the getter
approach.