cv-models
Use when defining, reading, writing or serializing data models with package:cv (CvModelBase, CvField, CvModelField, CvModelListField, cvAddConstructor, toMap/fromMap, toJson/cv<T>()). Covers field declaration, null vs unset semantics, builder registration, nested models, lists, enums, DateTime encoding and JSON helpers, without code generation.
소스 정보
- 저장소
- tekartik/cv.dart
- 최근 소스 활동
- 2026년 9월 20일 15:40
- 감지된 SKILL.md 언어
- 영어
- 스타
- 7
- 포크
- 1
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
파일 탐색기
2 개 파일SKILL.md 표시 중
SKILL.md
소스 지침 · 읽기 전용 미리보기- name
- cv-models
- description
- Use when defining, reading, writing or serializing data models with package:cv (CvModelBase, CvField, CvModelField, CvModelListField, cvAddConstructor, toMap/fromMap, toJson/cv<T>()). Covers field declaration, null vs unset semantics, builder registration, nested models, lists, enums, DateTime encoding and JSON helpers, without code generation.
# cv models
`package:cv` maps Dart objects to `Map<String, Object?>` and back with no code
generation. A model is a class whose fields are `CvField` objects. The field
holds the serialized key and a mutable value. The model lists its fields once
and gets `toMap()`, `fromMap()`, `==`, `clone()`, `copyFrom()` and JSON for free.
## Guidelines
* Extend `CvModelBase`. Declare each field as `final x = CvField<T>('key');`
and override `CvFields get fields => [...]` listing every field. A field not
in `fields` is never serialized.
* Read and write values through the field: `model.title.v` (alias `.value`).
Use `.valueOrThrow` when a value is required, `.valueOrNull` when optional.
* Distinguish unset from null. An unset field is absent from `toMap()`. A field
set to null with `.v = null` is present with a null value. `setValue(null)`
unsets unless `presentIfNull: true`. `clear()` unsets. `hasValue` is true for
present values, including null. `isNull` is true for both unset and null.
* `fromMap()` merges: only keys present in the map are written, other fields
keep their value. Call `clear()` first, or build a new object with
`map.cv<T>()`, when you need a full replacement.
* Register every model once with `cvAddConstructor(MyModel.new)` (or
`cvAddConstructors([...])`) before calling `map.cv<T>()`,
`json.cv<T>()`, `cvNewModel<T>()` or `clone()`. A missing registration throws
`CvBuilderException` with "Missing builder for 'T'". Group registrations in
an idempotent `initXxxBuilders()` function per package.
* Field types must be `String`, `int`, `double`, `num`, `bool`, `DateTime`,
`List`, `Map`, an enum or another `CvModel`. Use `CvListField<T>` for lists
of basic types, `CvModelField<T>` for one nested model, `CvModelListField<T>`
for a list of models and `CvModelMapField<T>` for a `Map<String, T>` of
models. Nested model types need a registered constructor too.
* Numbers read from a map are coerced to the field type (`1.7` into
`CvField<int>` gives `2`, `1` into `CvField<double>` gives `1.0`).
* Use `CvField.encodedEnum('key', MyEnum.values)` to store an enum as its
name string, and `CvField.encodedDateTime('key')` to store a `DateTime` as
an ISO 8601 string. A plain `CvField<DateTime>` keeps the `DateTime` object
in the map, which is fine for sembast or Firestore but not for JSON.
* Import `package:cv/cv_json.dart` (re-exports `cv.dart`) to get `toJson()`,
`toJsonPretty()`, `String.cv<T>()` and `String.cvList<T>()`.
* Equality (`==`) and `cvModelsAreEquals` compare `toMap()` content deeply, so
two models with the same values are equal.
* Prefer `CvFields` (a `List<CvField>`) as the type of `fields`.
* Put derived helpers in an `extension MyModelExt on MyModel` rather than in
the class, and keep the class limited to field declarations.
## Examples
### Define a model and convert it
```dart
import 'package:cv/cv.dart';
class Note extends CvModelBase {
final title = CvField<String>('title');
final content = CvField<String>('content');
final date = CvField<DateTime>('date');
@override
CvFields get fields => [title, content, date];
}
void main() {
var note = Note()
..title.v = 'My note'
..content.v = 'My note context'
..date.v = DateTime(2021, 08, 16);
print(note.toMap());
// {title: My note, content: My note context, date: 2021-08-16 00:00:00.000}
note = Note()..fromMap({'title': 'Other'});
print(note.title.v); // Other
print(note.content.hasValue); // false
}
```
### Register constructors and build from a map
```dart
void initNoteBuilders() {
cvAddConstructor(Note.new);
}
void main() {
initNoteBuilders();
var note = {'title': 'My note'}.cv<Note>();
var notes = [<String, Object?>{'title': 'a'}, {'title': 'b'}].cv<Note>();
var empty = cvNewModel<Note>();
var copy = note.clone();
}
```
### Nested models and lists
```dart
class Point extends CvModelBase {
final x = CvField<int>('x');
final y = CvField<int>('y');
@override
CvFields get fields => [x, y];
}
class Shape extends CvModelBase {
final origin = CvModelField<Point>('origin');
final points = CvModelListField<Point>('points');
final tags = CvListField<String>('tags');
final named = CvModelMapField<Point>('named');
@override
CvFields get fields => [origin, points, tags, named];
}
void main() {
cvAddConstructors([Point.new, Shape.new]);
var shape = {
'origin': {'x': 0, 'y': 0},
'points': [
{'x': 1, 'y': 2},
{'x': 3, 'y': 4},
],
'tags': ['a', 'b'],
}.cv<Shape>();
print(shape.points.v![1].y.v); // 4
// Building nested content in code
shape.origin.v = Point()
..x.v = 10
..y.v = 20;
shape.points.v = [Point()..x.v = 1];
// Or create an empty child through the field:
var child = shape.points.create({})..x.v = 5;
shape.points.v!.add(child);
}
```
### Enum and DateTime encoded for JSON
```dart
enum Status { draft, published }
class Article extends CvModelBase {
final status = CvField.encodedEnum('status', Status.values);
final createdAt = CvField.encodedDateTime('createdAt');
@override
CvFields get fields => [status, createdAt];
}
void main() {
cvAddConstructor(Article.new);
var article = Article()
..status.v = Status.published
..createdAt.v = DateTime.utc(2024, 1, 2);
print(article.toMap());
// {status: published, createdAt: 2024-01-02T00:00:00.000Z}
print(article.status.v); // Status.published
}
```
### JSON
```dart
import 'package:cv/cv_json.dart';
void main() {
cvAddConstructor(Note.new);
var note = '{"title":"My note"}'.cv<Note>();
var notes = '[{"title":"a"},{"title":"b"}]'.cvList<Note>();
print(note.toJson()); // {"title":"My note"}
print(notes.toJson()); // [{"title":"a"},{"title":"b"}]
print(note.toJsonPretty());
}
```
### Null, unset and partial maps
```dart
var note = Note();
note.toMap(); // {}
note.title.v = null;
note.toMap(); // {title: null}
note.title.setValue(null);
note.toMap(); // {}
note.toMap(includeMissingValue: true); // {title: null, content: null, date: null}
// Only export or import some columns
var map = <String, Object?>{'title': 'Other', 'content': 'Body'};
var otherNote = Note();
note.toMap(columns: ['title']);
note.fromMap(map, columns: ['title', 'content']);
note.copyFrom(otherNote, columns: ['title']);
```
### Reuse fields through a mixin
```dart
mixin TimestampsMixin on CvModelBase {
final createdAt = CvField.encodedDateTime('createdAt');
final updatedAt = CvField.encodedDateTime('updatedAt');
CvFields get timestampFields => [createdAt, updatedAt];
}
class Post extends CvModelBase with TimestampsMixin {
final title = CvField<String>('title');
@override
CvFields get fields => [title, ...timestampFields];
}
```
## Common mistakes
* Forgetting to add a new field to `fields`. It silently stays out of the map.
* Calling `map.cv<T>()` before `cvAddConstructor(T.new)`.
* Mixing base classes in one `cvAddConstructors` call. Register `CvModelBase`
models and `CvMapModelBase` models in separate calls.
* Expecting `fromMap()` to reset fields absent from the map.
* Using `CvField<DateTime>` in a model that is sent as JSON. Use
`CvField.encodedDateTime` instead.
* Declaring a field as `CvField<MyModel>`. Use `CvModelField<MyModel>` so the
nested map is converted.
## More
See [references/advanced.md](references/advanced.md) for `CvMapModelBase`
(models that keep unknown keys), raw `Model` map helpers, tree paths
(`cvPath`, `cvTreeValueAtPath`), `withParent` flattened keys, multi-type
fields, and the test helpers in `package:cv/cv_matcher.dart` (`cvEquals`,
`cvEqualsDiffReport`, `fillModel`).
GitHub에서 보기