Flutter Data models are data classes that extend DataModel and are annotated with @DataRepository:
@DataRepository([TaskAdapter])
@JsonSerializable()
class Task extends DataModel<Task> {
@override
final int? id;
final String title;
final bool completed;
Task({this.id, required this.title, this.completed = false});
}
DataModel automatically registers new data classes within the framework and enforces the implementation of an id getter. Use the type that better suits you: int? and String? are the most common.
The json_serializable library is helpful but not required.
@JsonSerializable? You don’t need to declare fromJson or toJson@JsonSerializable? You must declare fromJson and toJsonIf you choose it, you can make use of @JsonKey and other configuration parameters as usual. A common use-case is having a different remote id attribute such as objectId. Annotating id with @JsonKey(name: 'objectId') takes care of it.
Here’s an example:
@freezed
@DataRepository([TaskAdapter])
class Task extends DataModel<Task> with _$Task {
Task._();
factory Task({
int? id,
required String name,
required BelongsTo<User> user,
}) = _Task;
factory Task.fromJson(Map<String, dynamic> json) => _$TaskFromJson(json);
}
Unions haven’t been tested yet.
In order to omit an attribute simply use @JsonKey(ignore: true).
In addition, various useful methods become available on the class:
final user = User(id: 1, name: 'Frank');
await user.save();
The call is syntax-sugar for Repository#save and takes the same arguments (except the model).
Or, saving locally (i.e. remote: false) with a sync API:
final user = User(id: 1, name: 'Frank');
user.saveLocal();
final user = await repository.findOne(1);
await user.delete();
It is syntax-sugar for Repository#delete and takes the same arguments (except the model).
Or, deleting locally (i.e. remote: false) with a sync API:
final user = User(id: 1, name: 'Frank');
user.deleteLocal();
final updatedUser = await user.find();
It’s syntax-sugar for Repository#findOne and takes the same arguments (except the model/ID).
Or, reloading locally (i.e. remote: false) with a sync API:
final user = User(id: 1, name: 'Frank');
final user2 = user.reloadLocal();
Used whenever we need to transfer identity to a model without identity (that is, without an ID).
final user = User(id: 1, 'Parker');
final user2 = user.copyWith(name: 'Frank').withKeyOf(user);
id will still be null but saving and retreiving will work:
await user2.save(remote: false);
final user3 = await user2.find();
// user3.id == 1
Any Dart file that wants to use these extensions must import the library.
import 'package:flutter_data/flutter_data.dart';
VSCode protip! Type Command + . over the missing method and choose to import!
You can also disable them by hiding the extension:
import 'package:flutter_data/flutter_data.dart' hide DataModelExtension;
An example where Staff and Customer are both Users:
abstract class User<T extends User<T>> extends DataModel<T> {
final String id;
final String name;
User({this.id, this.name});
}
@JsonSerializable()
@DataRepository([JSONAPIAdapter, BaseAdapter])
class Customer extends User<Customer> {
final String abc;
Customer({String id, String name, this.abc}) : super(id: id, name: name);
}
@JsonSerializable()
@DataRepository([JSONAPIAdapter, BaseAdapter])
class Staff extends User<Staff> {
final String xyz;
Staff({String id, String name, this.xyz}) : super(id: id, name: name);
}