Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter Class inheritance

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 a BaseModel

I have 2 repositories (will have more in the finished app) UserRepository, TimesheetRepository, these both extend BaseRepository

What I am trying to do: I would like to have the reusable code such as: getAll(), countAll() etc in the BaseRepository 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 returned Model.

The Error: As you can see from my code because the BaseRepository is returning a BaseModel type, when I call the all() 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,
    );
  }
}

like image 294
Billy Mahmood Avatar asked Sep 04 '19 09:09

Billy Mahmood


People also ask

Does Dart have inheritance?

Dart has single inheritance. Read more about extending classes, the optional @override annotation, and more.

Is multiple inheritance possible in Flutter?

No, Dart does not support multiple implementation inheritance. Dart has interfaces, and like most other similar languages it has multiple interface inheritance. For implementation, there is only a single super-class chain that a class can inherit member implementations from.

Which type of inheritance is supported in Dart?

Introduction to the Dart inheritance Inheritance allows you to define a class that extends the functionality of another class. Dart supports single inheritance. It means that a class can inherit from a single class. Dart doesn't support multiple inheritances.


1 Answers

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.

like image 158
haroldolivieri Avatar answered Nov 22 '22 09:11

haroldolivieri