Flutter Class inheritance

6,476

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.

Share:
6,476
Billy Mahmood
Author by

Billy Mahmood

Updated on December 13, 2022

Comments

  • Billy Mahmood
    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 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,
        );
      }
    }
    
    
  • Billy Mahmood
    Billy Mahmood over 4 years
    This returns in this error The name T isnt a type so it cant be used as a type
  • Billy Mahmood
    Billy Mahmood over 4 years
    Thank 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
    Billy Mahmood over 4 years
    Hi, 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]