Ack Generator
ack_generator supports two modern directions: @AckInfer() turns a
top-level Ack schema into an immutable model, while @AckModel() derives an
Ack codec schema from a hand-written class. It also retains the deprecated Ack
1.1 @AckType() generator unchanged.
Schema-first usage
import 'package:ack/ack.dart';
import 'package:ack_annotations/ack_annotations.dart';
part 'user_schema.ack.dart';
part 'user_schema.ack.g.dart';
@AckInfer()
final userSchema = Ack.object({
'name': Ack.string(),
'email': Ack.string().email(),
});
Run dart run build_runner build. A declaration ending in Schema loses that
suffix, so userSchema generates User:
void main() {
final user = User.parse({
'name': 'Ada',
'email': 'ada@example.com',
});
print(user.name); // String
print(user.toJson()); // {'name': 'Ada', 'email': 'ada@example.com'}
}
Constructors don't validate immediately. Use parse for untrusted input;
toJson and safeToJson validate a directly constructed model while encoding
it. Generated models don't implement Map, and there are no fromMap or
toMap aliases.
Omit name when the inferred class name is right. Use
@AckInfer(name: 'Member') only when you need an exact custom name. Custom
names must be unchanged UpperCamelCase identifiers.
Class-first usage
In a class-first library, keep the model in source and apply the generated mixin:
import 'package:ack/ack.dart';
import 'package:ack_annotations/ack_annotations.dart';
part 'account.ack.dart';
part 'account.ack.g.dart';
@AckModel()
final class Account with _$AccountAck {
const Account({required this.name});
@MinLength(2)
final String name;
static final fromJson = AccountSchema.fromJson;
}
After generation, the public facade and model JSON methods use the same Ack codec boundary:
void main() {
final account = Account.fromJson({'name': 'Ada'});
print(account.toJson());
print(AccountSchema.toJsonSchema());
}
Schema support
The generator supports objects, empty objects, scalar and collection roots,
literals, enums, defaults, additional properties, built-in and custom
bidirectional codecs, named nested models, aliases, named Ack.lazy recursion,
and same-library discriminated unions. Lists, sets, and maps stored by a model
generated with @AckInfer() are copied recursively into unmodifiable
collections. @AckModel() parsing provides the same guarantee, including for
captured extras. Hand-written constructors and collection replacements passed
to copyWith remain responsible for their own defensive copies; use
deepUnmodifiableJsonMap for dynamic JSON maps. Raw
Ack.object(..., additionalProperties: true) schemas preserve extras, while a
class-first model applies its later unknownProperties projection. Use
discard only for tolerant, read-only consumers and capture for round trips.
Generation rejects shapes without a useful static, encodable model contract:
- one-way
.transform()calls, including.trim(),.toLowerCase(), and.toUpperCase(); use.codec()with an encoder; - nullable roots;
- nullable
Ack.listitem schemas and automatically inferredList<T?>orSet<T?>fields; make the collection nullable instead, or use an explicit@AckField(schema: ...)codec for a different collection contract; Ack.any(),Ack.anyOf(), and bareAck.instance<T>();- anonymous inline object fields and unresolved dynamic schema factories;
- invalid names, generated-member collisions, and cross-library union branches.
Named model references work through direct imports, prefixes, and re-exports.
Nested conversion uses each model's public $ack adapter so codec runtime
values aren't parsed twice.
JSON serialization
Every annotated library declares both parts:
part 'account.ack.dart';
part 'account.ack.g.dart';
Ack owns schema validation, defaults, codecs, union dispatch, and the public
parse / fromJson / toJson methods. json_serializable generates the
structural _$ClassFromJson / _$ClassToJson helpers into the Ack JSON
part. Ack-only apps do not add json_annotation or json_serializable;
ack_generator activates that second phase itself.
When a modern-only target also uses an ordinary source-gen builder that owns
.g.dart, disable the unused legacy builder in that target so it remains the
sole .g.dart owner:
targets:
$default:
builders:
ack_generator:ack_generator:
enabled: false
Supported declarations
@AckInfer() can annotate top-level schema variables and top-level schema
getters. Classes, instance members, and local variables are rejected.
@AckModel() annotates public, constructable final class declarations whose
stored fields are final. Annotated sealed union bases remain supported, and
their concrete branches must also be final. See the
Model Code Generation guide
for both directions, field inference, sealed unions, passthrough properties,
and build configuration.
For a hand-written Account, class-first generation exposes an
AccountSchema facade backed by a private _accountSchema codec. The facade
provides parsing, safe parsing, encoding, JSON Schema/schema-model export,
typed schema, and raw wireSchema. Instantiable models apply the generated
_$AccountAck mixin, which supplies toJson, copyWith (omitted means keep;
explicit null clears a nullable field), and deep collection-aware equality. Add
static final fromJson = AccountSchema.fromJson; when the class should expose
the conventional one-argument entry point. Imported nested models compose as
prefix.AddressSchema.schema. Across all imports and barrel exports,
show/hide combinators must expose both the authored declaration and its
generated companion (Address plus AddressSchema for class-first, or
addressSchema plus Address for schema-first). Visibility may be split
across multiple imports that use the same prefix.
Class-first wire-name overrides support @JsonKey(name: 'wire_name') on the
field. Other JsonKey options and constructor-parameter placement fail
generation so schema validation and JSON mapping remain identical.
For design details and migration notes, see the model and schema generation architecture.
Deprecated AckType compatibility
An unchanged Ack 1.1 declaration still uses part 'file.g.dart'; and generates
the same *Type, .args, Map, parse, and safeParse APIs. AckType is frozen
until its removal in Ack 2.0. New connected model graphs must use AckInfer or
AckModel; nested references between legacy and modern graphs are rejected
with a migration diagnostic.
| Legacy | Modern opt-in |
|---|---|
@AckType() |
@AckInfer() |
file.g.dart |
file.ack.dart + file.ack.g.dart |
UserType.parse(...) |
User.parse(...) or User.fromJson(...) |
Map access and .args |
Typed fields, .additionalProperties, and toJson() |