flutter/data-model
>- Add or update a dart_mappable data model in lib/src/models/content/, including adding format validation, regenerating code, and adding tests. Use when adding a new content type or modifying an existing model's fields.
npx skills add https://github.com/flutter/website --skill data-model
Add or update a typed content model in this project.
Follow the established conventions below as much as possible.
its source YAML or JSON file(s), and its test group first.
the structure of the YAML file it will load from and map to.
If the YAML file doesn't exist, ask the user to create and reference it
or provide information about its expected structure.
All data models live in the lib/src/models/content directory and
should roughly follow this general structure:
import 'package:dart_mappable/dart_mappable.dart';
import 'content_validation.dart';
part '<filename>.mapper.dart';
/// Doc comment: What this model represents and where it's loaded from.
///
/// Expected data format:
/// - `field_name`: Description and constraints of the expected value.
@MappableClass()
class ModelName with ModelNameMappable {
ModelName({
required this.fieldOne,
required this.fieldTwo,
this.optionalField,
}) {
// Validate all fields beyond their Dart type.
checkFormat(isNotBlank(fieldOne), 'field_one must be a non-empty string.');
// For optional fields, only validate when present:
if (optionalField case final optionalField?) {
checkFormat(isNotBlank(optionalField), 'optional_field must be a non-empty string.');
}
}
/// Each field should have a doc comment that follows Effective Dart.
final String fieldOne;
// Use `@MappableField` when the YAML key differs from the Dart field name.
@MappableField(key: 'field_two')
final String fieldTwo;
// Optional fields are nullable and not marked `required` in the constructor.
final String? optionalField;
// Every model must have this static factory. Use the generated mapper.
static ModelName fromJson(Map<String, Object?> json) => ModelNameMapper.fromMap(json);
}
When adding a new data model,
consider checking the format of one or more existing models
in the lib/src/models/content/ directory.
Effective Dart and other Dart best practices and standards.
content_validation.dart library anduse its helper functions to validate the contents of each field.
part directive must point to <filename>.mapper.dart.That part file is generated when running build_runner.
<ClassName>Mappable.That mixin is generated when running build_runner.
fromJson factory delegates to <ClassName>Mapper.fromMap(json).@MappableField(key: 'yaml_key') when the YAML key usessnake_case or otherwise differs from the camelCase Dart field name.
create and use a new enum for the Dart type.
To be serialized and deserialized, annotate the enum with @MappableEnum.
create a private MappingHook subclass in the same file.
final.following Effective Dart guidelines for doc comments.
listing each field, its brief meaning, and its constraints.
When users might make a mistake or a specific format is needed,
fields should be validated in the constructor body,
not just by Dart's type system.
Validation helpers are defined in
the lib/src/models/content/content_validation.dart library.
Some common validations include:
| Field type | Validation |
|------------------|----------------------------------------------------------------------|
| Required string | checkFormat(isNotBlank(field), '...') |
| Optional string | if (field case final field?) checkFormat(isNotBlank(field), '...') |
| Absolute URL | checkFormat(isAbsoluteHttpUrl(field), '...') |
| Image asset path | checkFormat(looksLikeImageAssetPath(field), '...') |
| Hex color | checkFormat(isHexColor(field), '...') |
| Non-empty list | checkFormat(field.isNotEmpty, '...') |
As appropriate, multiple validations can be completed.
Complete the less specific ones first, such as not being blank.
If you think any other validation is needed or might be helpful:
content_validation.dart library.content_validation.dart library..mapper.dart file: dart run build_runner build --delete-conflicting-outputs
test/models/content/content_models_test.dart.Read the file to understand the existing testing patterns,
import the new model, and add a test group following a similar pattern.
The test file already defines a few custom matchers.
If needed, reuse them, don't unnecessarily redefine them.
_throwsMapperException:For fields that are missing or have the wrong type.
_throwsValidationError:For fields that exist but fail validation.
dart test test/models/content/content_models_test.dart
dart analyze lib/src/models/content/
If you already are or want to use the data in a page, access the data using
the decodeJsonList and decodeJsonObject extension methods on BuildContext.
At the beginning of a page component's build method,
use the extension methods and store the data in a variable for reuse.
// For a list of items:
final items = context.decodeJsonList('page_name.data_key', ModelName.fromJson);
// For a single object:
final item = context.decodeJsonObject('page_name.data_key', ModelName.fromJson);
The dot-notation path maps to the content directory structure. For example,
page_name.data_key resolves to content/page_name/data_key.yaml.
Take flutter/data-model from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.