Why
json['name'] is dynamic. The as String cast throws when the value is null, missing, or a different type (for example an int id sent as a string by some backends).
Make the field nullable or give a default
class Product {
Product({required this.id, required this.name, this.note, required this.price});
final int id;
final String name;
final String? note;
final double price;
factory Product.fromJson(Map<String, dynamic> json) => Product(
id: (json['id'] as num).toInt(),
name: json['name'] as String? ?? '',
note: json['note'] as String?,
price: double.tryParse('${json['price']}') ?? 0,
);
}
as String? allows null.
(x as num).toInt() accepts both 5 and 5.0.
double.tryParse('${...}') handles "12.50" and 12.5 alike.
Let code generation do it
With json_serializable:
@JsonSerializable()
class Product {
Product({required this.id, required this.name, this.note});
final int id;
@JsonKey(defaultValue: '')
final String name;
final String? note;
factory Product.fromJson(Map<String, dynamic> j) => _$ProductFromJson(j);
Map<String, dynamic> toJson() => _$ProductToJson(this);
}
Run dart run build_runner build.
Lists and nested objects
final items = (json['items'] as List<dynamic>? ?? [])
.map((e) => Item.fromJson(e as Map<String, dynamic>))
.toList();
Pitfalls
- Dates: parse with
DateTime.tryParse and handle null.
- Never trust the API contract in production; log the raw body when parsing fails.
- Paste a sample response into the JSON to Dart converter to get a starting model, then adjust nullability by hand.