Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
ab88d44
wip(ack): pull typed-codecs library onto new main
leoafarias May 20, 2026
7999ee3
fix(ack): wire typed-codecs onto AckSchemaModel boundary
leoafarias May 20, 2026
f13bd66
test(ack): port typed-codecs test suite onto new main
leoafarias May 20, 2026
a6ebf25
test(ack): align suite with PR #107/108 + typed-codecs adoption
leoafarias May 20, 2026
c49aa08
test(ack_json_schema_builder): align with PR #107/108 + typed-codecs
leoafarias May 20, 2026
51223dd
style: dart format
leoafarias May 20, 2026
41fbb5a
fix(ack): correct schema export semantics
leoafarias May 20, 2026
c0acfce
test(example): align enum parsing with typed codecs
leoafarias May 21, 2026
197ac6a
refactor(ack): simplify datetime constraint routing
leoafarias May 21, 2026
ee53e44
chore: commit workspace changes
leoafarias May 21, 2026
0f4bb4b
refactor(ack): tighten public surface, narrow FluentSchema, drop dead…
leoafarias May 21, 2026
69d399f
chore: finalize codec implementation updates
leoafarias May 21, 2026
b9b60d3
Implement schema codec changes
leoafarias May 21, 2026
28b7e52
refactor(ack): lift schema null and encode defaults
leoafarias May 21, 2026
41c0220
chore: commit all pending changes
leoafarias May 21, 2026
a1e876a
refactor(ack): rename default resolution and route JSON adapters thro…
leoafarias May 21, 2026
c61d10d
fix(ack): let anyOf branches resolve defaults on null parse
leoafarias May 22, 2026
0efc6ee
feat: add Flutter value codecs for ACK schemas including color, offse…
leoafarias May 25, 2026
14746f5
feat(ack): add Ack.enumCodec for uniform CodecSchema enums
leoafarias May 26, 2026
46503d4
feat(ack)!: reject non-finite numeric schemas
leoafarias May 26, 2026
6f366c2
feat(flutter_codec): add geometry codecs
leoafarias May 26, 2026
c1c7bc4
merge: bring flutter codec work into codec implementation
leoafarias May 26, 2026
5b3bb94
fix(ack): align finite constraint metadata
leoafarias May 26, 2026
4ccce1d
merge: bring codec implementation into flutter codec
leoafarias May 26, 2026
d06d7f7
chore: remove flutter codec package from implementation branch
leoafarias May 26, 2026
83920eb
merge: keep flutter codec package on flutter branch
leoafarias May 26, 2026
bd917bb
refactor: replace finite number constraints with general number const…
leoafarias May 26, 2026
9569e2c
refactor(ack)!: remove ObjectSchema.model() in favor of .codec()
leoafarias May 26, 2026
0c1650f
feat(flutter_codec): add Shadow, BoxShadow, and Gradient family codecs
leoafarias May 26, 2026
a22268f
Merge branch 'feat/codec-implementation' into feat/flutter-codec
leoafarias May 26, 2026
29915d5
feat(flutter_codec): add codecs for FontWeight, Locale, TextDecoratio…
leoafarias May 26, 2026
bb392b9
feat(flutter_codec): enhance codecs for Gradient, FontWeight, Locale,…
leoafarias May 26, 2026
794668b
feat(json_readers): introduce utility functions for reading JSON fiel…
leoafarias May 26, 2026
6262927
feat(flutter_codec): add BoxDecoration codec
leoafarias May 26, 2026
7b5a2ad
feat(ack)!: union-owned discriminator injects key on encode
leoafarias May 27, 2026
b60633a
feat(flutter_codec): add ImageProvider, DecorationImage, and Rect codecs
leoafarias May 27, 2026
453c78b
feat(flutter_codec): add FontFeature and FontVariation codecs
leoafarias May 27, 2026
257a8d6
feat(flutter_codec): add ShapeBorder family + ShapeDecoration + Decor…
leoafarias May 27, 2026
a7fffdf
docs(flutter_codec): rewrite README with coverage table and seed CHAN…
leoafarias May 27, 2026
940126d
feat(flutter_codec)!: add RoundedSuperellipseBorder codec
leoafarias May 27, 2026
628f5fd
refactor(flutter_codec): drop redundant widening wrappers from discri…
leoafarias May 27, 2026
a38486c
docs(flutter_codec): document why ColorFilter and ImageFilter are exc…
leoafarias May 27, 2026
79b792b
feat(flutter_codec): add textHeightBehaviorCodec and strutStyleCodec
leoafarias May 27, 2026
66647b8
feat(flutter_codec): add StarBorder, LinearBorder, LinearBorderEdge c…
leoafarias May 27, 2026
d79702d
feat: Add codecs for Flutter widgets and constraints
leoafarias May 27, 2026
c8cf2ea
refactor(flutter_codec): use Ack.lazy for recursive widget child
leoafarias May 28, 2026
e03246f
Merge origin/main into feat/flutter-codec
leoafarias May 28, 2026
c752d8f
chore: register packages/flutter_codec in workspace
leoafarias May 28, 2026
96f6e7b
Merge remote-tracking branch 'origin/main' into feat/flutter-codec
leoafarias May 28, 2026
7059b86
Merge remote-tracking branch 'origin/main' into feat/flutter-codec
leoafarias May 29, 2026
e9ba420
fix(flutter_codec): correct version floor, round-trip bugs, loud-fail…
leoafarias Jun 9, 2026
fe2ef4a
test(flutter_codec): add golden JSON fixtures for every codec
leoafarias Jun 10, 2026
1ed589d
Merge remote-tracking branch 'origin/main' into feat/flutter-codec
leoafarias Jun 11, 2026
c51d60b
fix(flutter_codec): enforce codec invariants, fail loudly on unencoda…
leoafarias Jun 11, 2026
70bb356
Merge remote-tracking branch 'origin/main' into feat/flutter-codec
leoafarias Jun 27, 2026
b2a1f93
Merge remote-tracking branch 'origin/main' into feat/flutter-codec
leoafarias Jul 5, 2026
c11883c
Harden flutter_codec validation
leoafarias Jul 5, 2026
88f1d1a
Prepare flutter_codec for publish
leoafarias Jul 5, 2026
ee190dd
Clean up flutter_codec DCM lint findings in lib/
leoafarias Jul 5, 2026
fbe780e
feat(flutter_codec): complete reference integration
leoafarias Jul 13, 2026
a5e40f0
test(flutter_codec): pin public schema contract
leoafarias Jul 13, 2026
0eb07ca
fix(flutter_codec): harden validation diagnostics
leoafarias Jul 20, 2026
483fb0f
fix(flutter_codec): reject empty StrutStyle fallback with package
leoafarias Sep 4, 2026
2733087
Merge origin/main into feat/flutter-codec
leoafarias Sep 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
- `packages/ack_generator`: build_runner generator + unit/integration tests.
- `packages/ack_firebase_ai`: Firebase AI schema adapter.
- `packages/ack_json_schema_builder`: JSON Schema adapter.
- `packages/flutter_codec`: ACK codecs for portable Flutter value types.
- `example`: sample usage.

## Environment and setup
- Required SDKs: Dart `>=3.9.0 <4.0.0`, Flutter `>=3.35.0` (see `/pubspec.yaml`).
- Required SDKs: Dart `>=3.9.0 <4.0.0`, Flutter `>=3.41.0` (see `/pubspec.yaml`).
- CI pins Flutter to the version in `/.fvmrc`. Add a new version to `/.github/flutter-releases.json` with its checksum before a workflow may install it.
- Use from repo root:
1. `dart pub get`
Expand Down
4 changes: 4 additions & 0 deletions .github/flutter-releases.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
"dart": "3.9.0",
"sha256": "ff2d9474d768fdb61c1b8d6b76fd4eb98235c271e68b10abf41bd9fb41bc273d"
},
"3.41.0": {
"dart": "3.11.0",
"sha256": "368ae5b6993c51861324e704c42d61c4e290fba7213a88fd0e9fec15ded54599"
},
"3.41.2": {
"dart": "3.11.0",
"sha256": "4a04f8a6152986d14fc137ffaf98106ca743c0f9ab66f1bc2f20ee84eb573e5c"
Expand Down
21 changes: 21 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,24 @@ jobs:
test/schemas/extensions/numeric_extensions_test.dart \
test/schemas/extensions/list_schema_extensions_test.dart \
test/utils/collection_utils_test.dart

test-minimum:
name: Flutter 3.41.0
runs-on: ubuntu-24.04
timeout-minutes: 45
steps:
- name: Checkout repository
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false

- name: Set up the minimum workspace Flutter SDK
uses: ./.github/actions/setup-flutter
with:
flutter-version: '3.41.0'

- name: Resolve workspace dependencies
run: dart pub get

- name: Analyze and test every package
run: dart run melos run ci --no-select
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ This repository is a monorepo containing:
- **[ack_generator](./packages/ack_generator)**: Generates models from schemas and schemas from hand-written models
- **[ack_firebase_ai](./packages/ack_firebase_ai)**: Firebase AI (Gemini) schema converter for structured-output generation
- **[ack_json_schema_builder](./packages/ack_json_schema_builder)**: Converter to `json_schema_builder` schemas
- **[flutter_codec](./packages/flutter_codec)**: ACK codecs for portable Flutter painting, rendering, and widget values
- **[example](./example)**: Example projects demonstrating usage of all packages

## Community and support
Expand Down
1 change: 1 addition & 0 deletions llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ There is no `@AckSchema()` annotation; `AckSchema` is the runtime schema type.
3. `ack_generator`: generates models from schemas and schemas from models
4. `ack_firebase_ai`: converts Ack schemas to Firebase AI structured-output schemas
5. `ack_json_schema_builder`: converts Ack schemas to `json_schema_builder` schemas
6. `flutter_codec`: ACK codecs for portable Flutter painting, rendering, and widget values

## Core runtime usage

Expand Down
56 changes: 56 additions & 0 deletions packages/flutter_codec/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Changelog

## 0.1.1

- Harden codec-boundary validation for Flutter values that only assert in
debug/release-unsafe code paths: recursive `Container.child` nesting is
capped, `StarBorder` rejects point/valley rounding sums above `1`, gradient
stops must be within `[0, 1]` and ascending, `TextStyle.fontSize` must be
positive, and `StrutStyle.package` requires a `fontFamily` or a non-empty
`fontFamilyFallback` (empty fallback is treated as omitted, matching
Flutter). Encoding `Text.rich` now fails with a dedicated unsupported-span
diagnostic instead of a generic missing-`data` error.
- Document that the `EdgeInsets` primitive intentionally remains permissive;
widget codecs enforce non-negative inset rules where Flutter asserts them.

## 0.1.0

Initial release. JSON value codecs for Flutter's painting and rendering layers,
plus a small set of widget codecs, built on [`ack`](../ack/README.md).
Requires Flutter `>=3.41.0`.

- **Primitives**: `Color`, `Offset`, `Radius`, `Rect`, `Alignment` /
`AlignmentDirectional` / `AlignmentGeometry`, `EdgeInsets` /
`EdgeInsetsDirectional` / `EdgeInsetsGeometry`, `BorderRadius` /
`BorderRadiusDirectional` / `BorderRadiusGeometry`, `FontWeight`,
`FontFeature`, `FontVariation`, `TextDecoration`, `Locale`.
- **Enums**: 30+ painting / rendering / widget enums in a single
`lib/src/enums.dart` (e.g. `blendModeCodec`, `boxShapeCodec`,
`tileModeCodec`, `fontStyleCodec`).
- **Borders**: `BorderSide`, `Border`, `BorderDirectional`, `BoxBorder`,
`StrokeAlign`.
- **Shape borders** (discriminated by `"type"`): `CircleBorder`,
`StadiumBorder`, `RoundedRectangleBorder`, `BeveledRectangleBorder`,
`ContinuousRectangleBorder`, `RoundedSuperellipseBorder`, `StarBorder`,
`LinearBorder` (with `LinearBorderEdge`) → `ShapeBorder`.
- **Shadows**: `Shadow`, `BoxShadow`.
- **Gradients** (discriminated by `"type"`): `LinearGradient`,
`RadialGradient`, `SweepGradient` → `Gradient`.
- **Image providers** (discriminated by `"type"`): `NetworkImage`,
`AssetImage` → `ImageProvider`.
- **Decoration image**: `DecorationImage` (composes `imageProviderCodec`,
`rectCodec`, and the relevant enum codecs).
- **Text style**: `TextStyle` (including `fontFeatures` and `fontVariations`
lists), `StrutStyle` (sibling layout style), `TextHeightBehavior`.
- **Decorations** (discriminated by `"type"`): `BoxDecoration`,
`ShapeDecoration` → `Decoration`.
- **Constraints**: `BoxConstraints`, `Constraints` (discriminated by `"type"`).
- **Matrix**: `Matrix4`.
- **Widgets**: `Container`, `Text`, and portable `Key` / `ValueKey` codecs,
plus a `widgetCodec` union (discriminated by `"type"`).

`FontWeight` accepts and emits arbitrary integer weights (`[1, 1000]`) for
variable fonts in addition to the `"w100"`–`"w900"` / `"normal"` / `"bold"`
aliases.

Every codec exposes `.parse`, `.safeParse`, `.encode`, and `.toJsonSchema`.
29 changes: 29 additions & 0 deletions packages/flutter_codec/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
BSD 3-Clause License

Copyright (c) 2025, Leo Farias
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
174 changes: 174 additions & 0 deletions packages/flutter_codec/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# flutter_codec

JSON value codecs for Flutter's painting and rendering layers — plus a small,
growing set of widget codecs (`Container`, `Text`, `Key`) — built on
[`ack`](https://pub.dev/packages/ack).

Every codec is an Ack `CodecSchema` and exposes the same surface:

```dart
codec.parse(json); // decode, throws on failure
codec.safeParse(json); // decode, returns SchemaResult
codec.encode(value); // encode to a JSON-safe map / scalar
codec.safeEncode(value); // encode, returns SchemaResult
codec.toJsonSchema(); // emit JSON Schema for downstream tooling
```

Codecs compose: composite types reuse their dependents, so a `BoxDecoration`
codec inherits the validation and schema output of `Color`, `BoxBorder`,
`Gradient`, `BoxShadow`, and so on.

## Quick example

```dart
import 'package:flutter/painting.dart';
import 'package:flutter_codec/flutter_codec.dart';

final decoration = BoxDecoration(
color: const Color(0xFF2196F3),
border: Border.all(color: const Color(0xFFFF0000), width: 2),
borderRadius: BorderRadius.circular(8),
gradient: const LinearGradient(
colors: [Color(0xFFFF0000), Color(0xFF0000FF)],
),
);

final json = boxDecorationCodec.encode(decoration);
// json is a Map<String, Object?> safe for jsonEncode

final roundTripped = boxDecorationCodec.parse(json);
assert(roundTripped == decoration);
```

## ACK patterns in this package

- [`colorCodec`](lib/src/primitives/color.dart) shows a custom codec with a
compact boundary and a rich Flutter runtime value.
- [`boxDecorationCodec`](lib/src/decorations.dart) composes child codecs and
applies defaults while keeping its canonical output explicit.
- [`gradientCodec`](lib/src/gradients.dart) combines literals, named
refinements, and a discriminated union.
- [`widgetCodec`](lib/src/widgets/widget.dart) uses `Ack.lazy` through its
recursive `Container` branch, with a bounded runtime depth.

Together, `safeParse`, `safeEncode`, and `toJsonSchema` provide the public
validation, encoding, and boundary-schema workflow for all of these patterns.

## Coverage

| Family | Type(s) | Codec(s) | Source |
|---|---|---|---|
| Primitives | `Color` | `colorCodec` | [lib/src/primitives/color.dart](lib/src/primitives/color.dart) |
| | `Offset` | `offsetCodec` | [lib/src/primitives/offset.dart](lib/src/primitives/offset.dart) |
| | `Radius` | `radiusCodec` | [lib/src/primitives/radius.dart](lib/src/primitives/radius.dart) |
| | `Rect` | `rectCodec` | [lib/src/primitives/rect.dart](lib/src/primitives/rect.dart) |
| | `Alignment` / `AlignmentDirectional` / `AlignmentGeometry` | `alignmentCodec`, `alignmentDirectionalCodec`, `alignmentGeometryCodec` | [lib/src/primitives/alignment.dart](lib/src/primitives/alignment.dart) |
| | `EdgeInsets` / `EdgeInsetsDirectional` / `EdgeInsetsGeometry` | `edgeInsetsCodec`, `edgeInsetsDirectionalCodec`, `edgeInsetsGeometryCodec` | [lib/src/primitives/edge_insets.dart](lib/src/primitives/edge_insets.dart) |
| | `BorderRadius` / `BorderRadiusDirectional` / `BorderRadiusGeometry` | `borderRadiusCodec`, `borderRadiusDirectionalCodec`, `borderRadiusGeometryCodec` | [lib/src/primitives/border_radius.dart](lib/src/primitives/border_radius.dart) |
| | `FontWeight` | `fontWeightCodec` | [lib/src/primitives/font_weight.dart](lib/src/primitives/font_weight.dart) |
| | `FontFeature` | `fontFeatureCodec` | [lib/src/primitives/font_feature.dart](lib/src/primitives/font_feature.dart) |
| | `FontVariation` | `fontVariationCodec` | [lib/src/primitives/font_variation.dart](lib/src/primitives/font_variation.dart) |
| | `TextDecoration` | `textDecorationCodec` | [lib/src/primitives/text_decoration.dart](lib/src/primitives/text_decoration.dart) |
| | `TextHeightBehavior` | `textHeightBehaviorCodec` | [lib/src/primitives/text_height_behavior.dart](lib/src/primitives/text_height_behavior.dart) |
| | `Locale` | `localeCodec` | [lib/src/primitives/locale.dart](lib/src/primitives/locale.dart) |
| Enums | 30+ painting/rendering enums (e.g. `blendModeCodec`, `boxShapeCodec`, `tileModeCodec`, `fontStyleCodec`) | see file | [lib/src/enums.dart](lib/src/enums.dart) |
| Borders | `BorderSide`, `Border`, `BorderDirectional`, `BoxBorder`, `StrokeAlign` | `borderSideCodec`, `borderCodec`, `borderDirectionalCodec`, `boxBorderCodec`, `strokeAlignCodec` | [lib/src/borders.dart](lib/src/borders.dart) |
| Shape borders | `CircleBorder`, `StadiumBorder`, `RoundedRectangleBorder`, `BeveledRectangleBorder`, `ContinuousRectangleBorder`, `RoundedSuperellipseBorder`, `StarBorder`, `LinearBorder`, `LinearBorderEdge`, `ShapeBorder` | `circleBorderCodec`, `stadiumBorderCodec`, `roundedRectangleBorderCodec`, `beveledRectangleBorderCodec`, `continuousRectangleBorderCodec`, `roundedSuperellipseBorderCodec`, `starBorderCodec`, `linearBorderCodec`, `linearBorderEdgeCodec`, `shapeBorderCodec` | [lib/src/shape_borders.dart](lib/src/shape_borders.dart) |
| Shadows | `Shadow`, `BoxShadow` | `shadowCodec`, `boxShadowCodec` | [lib/src/shadows.dart](lib/src/shadows.dart) |
| Gradients | `LinearGradient`, `RadialGradient`, `SweepGradient`, `Gradient` | `linearGradientCodec`, `radialGradientCodec`, `sweepGradientCodec`, `gradientCodec` | [lib/src/gradients.dart](lib/src/gradients.dart) |
| Image providers | `NetworkImage`, `AssetImage`, `ImageProvider` | `networkImageCodec`, `assetImageCodec`, `imageProviderCodec` | [lib/src/image_providers.dart](lib/src/image_providers.dart) |
| Decoration image | `DecorationImage` | `decorationImageCodec` | [lib/src/decoration_image.dart](lib/src/decoration_image.dart) |
| Text style | `TextStyle` | `textStyleCodec` | [lib/src/text_style.dart](lib/src/text_style.dart) |
| Strut style | `StrutStyle` | `strutStyleCodec` | [lib/src/strut_style.dart](lib/src/strut_style.dart) |
| Decorations | `BoxDecoration`, `ShapeDecoration`, `Decoration` | `boxDecorationCodec`, `shapeDecorationCodec`, `decorationCodec` | [lib/src/decorations.dart](lib/src/decorations.dart) |
| Constraints | `BoxConstraints`, `Constraints` | `boxConstraintsCodec`, `constraintsCodec` | [lib/src/constraints.dart](lib/src/constraints.dart) |
| Matrix | `Matrix4` | `matrix4Codec` | [lib/src/primitives/matrix4.dart](lib/src/primitives/matrix4.dart) |
| Widgets | `Container`, `Text`, `Key` (`ValueKey`) | `containerWidgetCodec`, `textWidgetCodec`, `keyCodec`, `widgetCodec` | [lib/src/widgets/](lib/src/widgets/) |

## Discriminated unions

Polymorphic types are encoded as `{ "type": "<branch>", ...fields }`. The
discriminator key is injected by the union at encode time. Most standalone
branch codecs do not require it on input; the gradient branches are the
exception — they embed a `"type"` literal in their own schema, so they self-tag
and accept (and require) the key on input as well.

| Union | Discriminator key | Branches |
|---|---|---|
| `gradientCodec` | `"type"` | `"linear"`, `"radial"`, `"sweep"` |
| `imageProviderCodec` | `"type"` | `"network"`, `"asset"` |
| `shapeBorderCodec` | `"type"` | `"circle"`, `"stadium"`, `"roundedRectangle"`, `"beveledRectangle"`, `"continuousRectangle"`, `"roundedSuperellipse"`, `"star"`, `"linear"` |
| `decorationCodec` | `"type"` | `"box"`, `"shape"` |
| `keyCodec` | `"type"` | `"value"` |
| `widgetCodec` | `"type"` | `"container"`, `"text"` |
| `constraintsCodec` | `"type"` | `"box"` |

## Intentionally excluded

These types have no portable JSON shape, or their JSON representation would
mislead more than it helps. Each is documented at the call site rather than
silently falling back.

- **Opaque `dart:ui` state — encode impossible via public API**:
`ColorFilter` and `ImageFilter`. `ColorFilter` keeps `_color`, `_blendMode`,
`_matrix`, and `_type` in library-private fields and exposes the same
`runtimeType` for all four constructor variants, so an existing instance
cannot be inspected back to JSON. `ImageFilter` is abstract with a private
constructor (`ImageFilter._()`) and returns library-private subtypes
(`_GaussianBlurImageFilter`, `_MatrixImageFilter`, etc.) from its factories
— external code cannot `is`-check or downcast them. The only state-revealing
surface is `toString()`, which is a debug format Flutter is free to change
between releases. A bidirectional codec is not achievable here without
introducing parallel descriptor types; the same goes for
`DecorationImage.colorFilter` (which embeds a `ColorFilter`). Because
`colorFilter` *is* part of `DecorationImage` equality, encoding a
`DecorationImage` that carries one **throws** rather than silently dropping it.
- **No portable JSON shape (encode throws)**: `Gradient.transform`
(`GradientTransform` is an open abstract type — encoding a transformed
gradient throws rather than dropping it silently). `Text.rich` / inline
`TextSpan` trees are also rejected on encode instead of dropping the span.
- **No portable JSON shape**: `Paint`, `Path`, `Shader`,
`TextStyle.foreground` / `TextStyle.background`,
`DecorationImage.onError`, `FlutterLogoDecoration`.
- **Local or recursive providers**: `FileImage` (local path), `MemoryImage`
(base64 bloat), `ResizeImage` (wraps another provider), custom
`AssetBundle` instances on `AssetImage`.
- **8-bit sRGB color**: `Color` encodes as `#RRGGBB` / `#AARRGGBB`. Integer sRGB
colors round-trip exactly, but sub-8-bit float-channel precision (from
`Color.withValues` / `Color.lerp`) is quantized and a non-sRGB `colorSpace`
(display P3, extended sRGB) is flattened to sRGB.
- **Lossy narrowing**: `OvalBorder` extends `CircleBorder`, so it round-trips
as `CircleBorder` — the runtime subtype is lost. The painted output is
equivalent to `CircleBorder(eccentricity: 1.0)`. Likewise `StarBorder.polygon`
round-trips as the equivalent regular `StarBorder` (its null
`innerRadiusRatio` becomes the resolved value), and `StarBorder.rotation`
survives only to floating-point precision (degrees↔radians). Both are
painted-equivalent but not `==`-equal.
- **Font-family `packages/` ambiguity**: a literal `fontFamily:
'packages/<pkg>/<x>'` supplied without a `package:` argument is read back as
package-qualified (the common case), so it does not round-trip under
`TextStyle` equality although the resolved family string is preserved.
- **Separate plans**: `InputBorder` family (Material — `OutlineInputBorder`,
`UnderlineInputBorder`).

## JSON Schema export

Every codec implements `.toJsonSchema()`, returning a `Map<String, Object?>`
that round-trips through `jsonEncode`. Composition flows through: the schema
for `boxDecorationCodec` embeds the schemas for its dependent codecs (color
pattern, gradient discriminator, shape enum, and so on).

Draft-7 output describes the portable boundary and the constraints that JSON
Schema can express. Cross-field Dart refinements, such as correlated gradient
stops or `Container` constructor invariants, and the `Ack.lazy` widget recursion
cap remain runtime-only. Use `safeParse` and `safeEncode` when those checks must
be enforced; exported JSON Schema alone does not include them.

## Roadmap

The painting- and rendering-layer surface is feature-complete for the types
Flutter exposes JSON-safely. The widget codecs (`Container`, `Text`, `Key`) are
a deliberately small surface that will grow over time. Further additions to the
painting layer would require either upstream changes to `dart:ui` (to expose
`ColorFilter` / `ImageFilter` state) or a parallel descriptor-type design that
we'd own outside the raw Flutter types.
16 changes: 16 additions & 0 deletions packages/flutter_codec/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Inherits the shared workspace config (lints + DCM presets/rules).
# See /analysis_options.yaml at the workspace root.
include: ../../analysis_options.yaml

analyzer:
language:
strict-casts: true
strict-inference: true
strict-raw-types: true

dart_code_metrics:
rules:
# Dot shorthands require Dart 3.10; this package's SDK floor is 3.8 (see
# pubspec.yaml). Same constraint as the workspace root's
# prefer-shorthands-with-enums override.
prefer-shorthands-with-static-fields: false
16 changes: 16 additions & 0 deletions packages/flutter_codec/lib/flutter_codec.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/// Flutter value codecs built on ACK schemas.
library;

export 'src/borders.dart';
export 'src/constraints.dart';
export 'src/decoration_image.dart';
export 'src/decorations.dart';
export 'src/enums.dart';
export 'src/gradients.dart';
export 'src/image_providers.dart';
export 'src/primitives.dart';
export 'src/shadows.dart';
export 'src/shape_borders.dart';
export 'src/strut_style.dart';
export 'src/text_style.dart';
export 'src/widgets.dart';
Loading
Loading