Guide
Flutter model classes with fromJson and toJson
Dart has no reflection in Flutter release builds, so JSON must be converted by explicit code: a factory that reads a map and a method that writes one. Writing those by hand for nested data is repetitive and easy to get subtly wrong. This generator writes complete, null-safe model classes from a sample, with working fromJson and toJson and no build step.
The shape of each class
Every object in the sample becomes a class with final fields and a const constructor using named parameters. Fields that had a value in every sample are non-nullable and required; fields that were missing or null somewhere are nullable and optional. Property names follow Dart’s camelCase, with the original key kept in the conversion code.
fromJson
The factory reads each key from a Map<String, dynamic> and converts it. Strings, integers and booleans are cast directly. Decimals are read as num and converted with toDouble(), because the decoder turns 5 into an int even when the field usually holds 5.5. Nested objects call the nested class’s fromJson, and lists are mapped item by item: (json['orders'] as List<dynamic>).map((e) => Order.fromJson(e as Map<String, dynamic>)).toList(). Nullable fields use null-aware versions of the same expressions.
toJson
Sending data back to an API needs the reverse. toJson writes every field under its original key, calls toJson() on nested objects and maps lists of objects, so jsonEncode(user) produces JSON with the same shape you started with.
No build_runner needed
Packages such as json_serializable generate the same code from annotations, which pays off for large apps with many changing models. Plain classes avoid the generator, the part files and the build step, and they are easy to read and debug. Start with these and move to code generation when the number of models grows.
A worked example
The sample user produces User, Order, Item and Address classes. In Order, the total is read as (json['total'] as num).toDouble(), the items as (json['items'] as List<dynamic>).map((e) => Item.fromJson(e as Map<String, dynamic>)).toList(), and toJson writes 'orders': orders.map((e) => e.toJson()).toList() in User.
Decoding in a Flutter app
Fetch with the http package, then User.fromJson(jsonDecode(response.body) as Map<String, dynamic>). For large responses, run the decoding in a background isolate with compute or Isolate.run so the interface does not stutter.
Refining the models
Replace string fields that hold fixed values with enums, add DateTime.parse for timestamps you want as dates, and consider equality with == and hashCode (or the equatable package) for models used in state management.
Handling errors while decoding
The casts in fromJson throw a TypeError when a value has an unexpected type, for example a string where an int was expected. Wrap decoding of external data in a try/catch at the point where the response arrives, log the raw body when it fails, and show a friendly message instead of a red error screen.
Root lists and pagination
When the response is a list, the root class describes one item; when it is wrapped with paging fields, the wrapper becomes the root class with a list of items inside, so page and total are typed as well.
Related tools
Generate the native models with JSON to Kotlin and JSON to Swift, TypeScript for a web client with JSON to TypeScript, or tidy the sample in the JSON Formatter.
Checked against the current version of the JSON to Dart Class on .

