Skip to content

Formatters & Code

JSON to Dart / Flutter Model

Dart classes with fromJson constructors for Flutter.

Runs in your browser
JSON · source
lines: 17chars: 261size: 261 B
Dart model · result
lines: 50chars: 1028size: 1.0 KB
live

Understanding JSON → Dart

fromJson, toJson, and a build runner.

Why Dart needs codegen for JSON at all, the two-step build runner workflow, and how Flutter projects keep their models maintainable.

Dart has no runtime reflection.

Dart (especially the AOT-compiled Flutter variant) has no general runtime reflection. There's no equivalent of Java's Class.forName or Python's __dict__ — you can't ask a class what fields it has at runtime. That makes the "annotate and parse" approach used by Jackson or Codable impossible. The alternatives are: write fromJson / toJson methods by hand, or use a build-time code generator. For anything beyond a handful of fields, codegen wins.

json_serializable is the standard answer.

The de-facto pattern: annotate your class with @JsonSerializable(), declare its fields, then run dart run build_runner build and a sibling my_class.g.dart file appears with the generated fromJson / toJson functions. The class itself stays thin; the generated code does the heavy lifting. Re-run the build runner whenever the class changes.

A worked example.

From { "user_id": 7, "name": "Q", "avatar": null }: import 'package:json_annotation/json_annotation.dart'; part 'user.g.dart'; @JsonSerializable() class User { @JsonKey(name: 'user_id') final int userId; final String name; final String? avatar; User({required this.userId, required this.name, this.avatar}); factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); Map<String, dynamic> toJson() => _$UserToJson(this); } The _$UserFromJson and _$UserToJson live in the generated .g.dart file. The class declares the shape; codegen does the parsing.

One class + one part file

@JsonSerializable() + part 'X.g.dart'

The .g.dart file is generated; commit it or rebuild on demand.

dart run build_runner build

= Round-trippable User class

Required and nullable.

Dart 2.12 added null safety. A property typed String cannot be null; one typed String? can. Constructor parameters marked required must be passed; ones with default values or nullable types can be omitted. The codegen tool's job: map non-null JSON fields to non-null Dart types, null-or-missing JSON fields to nullable types with optional constructor params.

Freezed for immutable models.

The freezed package builds on json_serializable to add immutability, copyWith, deep equality, and union-type support. For Flutter projects with non-trivial domain models, freezed is the standard upgrade — same codegen workflow, much richer generated API. The trade-off is one more code generator in the pipeline; the benefit is several hundred lines of hand-written boilerplate eliminated.

The build runner cost.

Code generation feels like magic until the build runner takes 30 seconds on a large project. dart run build_runner watch rebuilds incrementally on file changes, which keeps the loop tight. The generated files should be committed (so fresh checkouts don't need a regen before running) but treated as build artifacts — never hand-edited. Some projects gitignore the .g.dart files and regen on every CI run; both styles are valid.

Discriminated unions.

For an array of events with different shapes, freezed provides the cleanest pattern. @freezed sealed class Event with _$Event { const factory Event.click(int x, int y) = Click; const factory Event.submit(String form) = Submit; } The sealed class plus factory constructors gives exhaustive pattern matching via Dart 3's switch expressions. Codegen tools that detect a JSON discriminator should emit this shape; without it, you get a flat class with every field optional, which is the same anti-pattern as everywhere else.

Frequently asked questions

Quick answers.

Does this support Null Safety?

Yes. The generated code uses non-nullable types and required parameters where appropriate, matching modern Dart 2.12+ and Flutter standards.

How are nested JSON objects handled?

The tool recursively creates separate Dart classes for every nested object it finds to ensure your data model remains modular and clean.

Can I customise the root class name?

Yes. You can specify the name of the base class to match your data entity, such as `User` or `ProductResponse`.

Is my data sent to a server?

No. The conversion logic runs entirely within your browser sessions. Your JSON structure and variable names are never transmitted to our servers.

People also search for

Related tools

More in this room.

See all in Formatters & Code