Flutter Sqflite multiple table models Flutter Sqflite multiple table models dart dart

Flutter Sqflite multiple table models


I made a few comments advising the contrary, but I just remembered in my last project I did something like that, it was for Firebase Database, but it would quite similar for sqflite.

I created a BaseItem abstract class, with a key, and all models would descend from that one.

I also created a BaseProvider abstract class, which would require a BaseItem and would define the simple methods to access the model.

The upsert and delete methods lay in FirebaseBaseProvider, which extends BaseProvider.

I'll paste parts of it here (removing quite a lot to make it more understandable):

abstract class BaseItem {  const BaseItem({this.key});  final String key;}abstract class BaseProvider<T extends BaseItem> {  Future<List<T>> find();  Future<BaseKey> upsert(T item);  Future<int> delete(T item);}abstract class FirebaseBaseProvider<T extends BaseItem> {  // Abstract methods which need to be implemented  T fromMap(BaseKey key, dynamic map);  Map<String, dynamic> toJson(BaseKey key, T item);  Future<DatabaseReference> getReference(BaseKey base) async { ... }      BaseKey compileKey(T item, {String useKey}) { ... }  Future<List<T>> find() async {    List<T> result = new List();    // my implementation doesnt work like this,     // as it's firebase based, but this would    // be the place in a Sqflite implementation to use    // fromMap and load the items    return result;  }  Future<BaseKey> upsert(T item) async {    if (item == null) return null;    BaseKey key = compileKey(item);    (await getReference(key)).set(toJson(key, item));    return key;  }  Future<int> delete(T item) async {    if (item == null) return null;    if (item.key != null && item.key != "") {      (await getReference(compileKey(item))).remove();    }    return 0;  }}

Then, in order to implement a News model, or any other model, then I would create it by simply defining its contents, like that:

class News extends BaseItem {  News({String key, this.creation, this.messageSubject, this.messageBody}) : super(key: key);  final DateTime creation;  final String messageSubject;  final String messageBody;  bool operator ==(o) => o is News && (o.key == key);  int get hashCode => key.hashCode;}

And it would require its specific provider, that would implement only the toJson and fromMap methods, like that:

class NewsProvider extends FirebaseBaseProvider<News> {  @override  Map<String, dynamic> toJson(BaseKey key, News news) {    return {      "creation": news.creation,      "messageSubject": news.messageSubject,      "messageBody": news.messageBody,    };  }  @override  News fromMap(BaseKey key, dynamic map) {    DateTime creation = map["creation"] == null ? null : DateTime.tryParse(map["creation"] as String);    return new News(      key: key.child.key,      creation: creation,      messageSubject: map["messageSubject"] as String,      messageBody: map["messageBody"] as String,    );  }}

In the end, NewProvider is providing the find, upsert and delete methods, but their implementation lay on the abstract class, just one implementation of them for all the models, as you wanted.

Of course, my implementation is quite more complicated than that, both because Firebase requires a different approach to obtain/load the items and also as the find method ends up having to be different in each model specific provider. But yet, quite a lot can be simplified.

What I was saying in the comment is that this last class, the specific NewsProvider which have both toJson and fromMap specific implementations may also be generalized by using annotations in the News model class, but that brings quite a lot of problems and obscurity and - in my opinion, of course - it's not worth it.


I had a similar question about creating tables, all the examples just create one table. I found this webpage that created two tables putting an await on each command.

Like this:

    Future _onCreate(Database db, int version) async {        await db.execute("CREATE TABLE table1 (id INTEGER, valuex TEXT)");        await db.execute("CREATE TABLE table2 (id INTEGER, valuey TEXT)");        await db.execute("CREATE TABLE table3 (id INTEGER, valuez TEXT)");    }