Flutter Class inheritance
I wouldn't do the parse fromJson the way you're doing since you need to pass an empty instance of the model to be able to create a valid instance of the same object. But in order to have your architecture working you need to do some corrections:
1 - make usage of generics.
BaseRepository
abstract class BaseRepository<T extends BaseModel> {
BaseRepository({this.table, this.model});
final String table;
final T model;
// Retrieve all the items
Future<List<T>> all() async {
final sql = '''SELECT * FROM $table''';
final data = await db.rawQuery(sql);
return data.map((node) {
return model.fromJson(jsonData: node);
}).toList();
}
Future<T> findById(int id) async {
final sql = '''SELECT * FROM $table
WHERE id = ?''';
final data = await db.rawQuery(sql, [id]);
return model.fromJson(jsonData: data.first);
}
// Count all the items
Future<int> count() async {
final data = await db.rawQuery('''SELECT COUNT(*) FROM $table''');
int count = data[0].values.elementAt(0);
int idForNewItem = count++;
return idForNewItem;
}
// clear the table
Future<void> delete() async {
// truncate current database table
await db.rawQuery('''DELETE FROM $table''');
}
}
2 - correctly call the super constructor
TimesheetRepository
class TimesheetRepository extends BaseRepository<TimesheetModel> {
///IMHO you should not pass TimesheetModel instance here, it is really redundant
///you can create a parse class that receives the type and a json and does the
///trick
TimesheetRepository() : super(table: 'timesheets', model: TimesheetModel());
}
3 - add the correct return to your fromJson
method
abstract class BaseModel {
BaseModel fromJson({Map<String, dynamic> jsonData});
}
I could not test it integrated with the database, so let me know if that works.
Billy Mahmood
Updated on December 13, 2022Comments
-
Billy Mahmood over 1 year
I am building my first app in Flutter, this app is using the
sqlite database
. So I have models and repositories.The code layout:
I have 2 models (will have more in the finished app)
UserModel
,TimesheetModel
, these both extend aBaseModel
I have 2 repositories (will have more in the finished app)
UserRepository
,TimesheetRepository
, these both extendBaseRepository
What I am trying to do: I would like to have the reusable code such as:
getAll()
,countAll()
etc in theBaseRepository
that way all the repositories that extend the base repository have this functionality and all I should need to do is set the table name and set the returnedModel
.The Error: As you can see from my code because the
BaseRepository
is returning aBaseModel
type, when I call theall()
function on timesheet object, I get the following error : type 'List' is not a subtype of type 'List'I am not sure how to fix this, any suggestions please?
BaseRepository
abstract class BaseRepository { final String table; final model; BaseRepository({this.table, this.model}); // Retrieve all the items Future<List<BaseModel>> all() async { final sql = '''SELECT * FROM $table'''; final data = await db.rawQuery(sql); List<BaseModel> forms = List(); for (final node in data) { final form = model.fromJson(jsonData: node); forms.add(form); } return forms; } // Find an item by its ID Future findById(int id) async { final sql = '''SELECT * FROM $table WHERE id = ?'''; List<dynamic> params = [id]; final data = await db.rawQuery(sql, params); final form = model.fromJson(jsonData: data.first); return form; } // Count all the items Future<int> count() async { final data = await db.rawQuery('''SELECT COUNT(*) FROM $table'''); int count = data[0].values.elementAt(0); int idForNewItem = count++; return idForNewItem; } // clear the table Future<void> delete() async { // truncate current database table await db.rawQuery('''DELETE FROM $table'''); } }
TimesheetRepository
class TimesheetRepository extends BaseRepository { String table = 'timesheets'; TimesheetModel model = new TimesheetModel(); // Search for a item by its name Future<List<TimesheetModel>> findByDate(DateTime dateTime) async { final String date = DateFormat("yyyy-MM-dd").format(dateTime); final sql = '''SELECT * FROM $table WHERE timesheet_date = ?'''; List<dynamic> params = [date]; final data = await db.rawQuery(sql, params); List<TimesheetModel> forms = List(); for (final node in data) { final form = TimesheetModel.fromJson(jsonData: node); forms.add(form); } return forms; } // Add a new item Future<void> store(TimesheetModel timesheet) async { final sql = '''INSERT INTO $table ( user_id, timesheet_date, start_time, end_time, json, is_uploaded ) VALUES (?,?,?,?,?,?)'''; List<dynamic> params = [ timesheet.userId, DateFormat("yyyy-MM-dd").format(timesheet.timesheetDate), timesheet.startTime, timesheet.endTime, convert.json.encode(timesheet.json), timesheet.is_uploaded, ]; final result = await db.rawInsert(sql, params); DatabaseCreator.databaseLog('Add form', sql, null, result, params); } }
When calling all on Timesheet
TimesheetRepository timesheet = TimesheetRepository(); timesheet.all();
Base Model
abstract class BaseModel { fromJson(); }
Timesheet Model
class TimesheetModel extends BaseModel { int id; int userId; DateTime timesheetDate; String startTime; String endTime; Map json = { "task": "", "detail": "", "notes": "", }; bool is_uploaded; TimesheetModel({ this.id, this.userId, this.timesheetDate, this.startTime, this.endTime, this.json, this.is_uploaded, }); fromJson({Map<String, dynamic> jsonData}) { return TimesheetModel( id: jsonData['id'] as int, userId: jsonData['user_id'] as int, timesheetDate: timesheetDate, startTime: jsonData['start_time'], endTime: jsonData['end_time'], is_uploaded: hasUploaded, ); } }
-
Billy Mahmood over 4 yearsThis returns in this error
The name T isnt a type so it cant be used as a type
-
Billy Mahmood over 4 yearsThank you, I am going to implement this today and will be back to let you know how I got on, looks promising though.
-
Billy Mahmood over 4 yearsHi, I managed to try out the code, am not getting any errors however this script is returning null
dart return data.map((node) { model.fromJson(jsonData: node); }).toList();
I have 2 records in my database, so I get[null, null]