Free & open source — no account required
Free & open source — no account required
This technical guide provides an in-depth analysis of the json to flutter model engine, best practices for implementation, and data security standards.
Every Flutter app that consumes a REST API needs Dart model classes to deserialize JSON responses. Writing these by hand means transcribing field names, choosing the right Dart type for each JSON type, and wiring up fromJson() factory constructors — repetitive work that's easy to get wrong. Generating the class skeleton from your JSON sample gives you the field declarations and constructor instantly, leaving only the serialization methods and null-safety annotations to add.
// Input JSON
{
"id": "usr_4421",
"display_name": "Kenji Tanaka",
"email": "kenji@example.com",
"follower_count": 1840,
"is_verified": true,
"avatar_url": "https://cdn.example.com/avatars/4421.jpg",
"bio": null
}
// Generated Dart Class
class Root {
final String id;
final String display_name;
final String email;
final double follower_count;
final bool is_verified;
final String avatar_url;
final dynamic bio;
Root({
required this.id,
required this.display_name,
required this.email,
required this.follower_count,
required this.is_verified,
required this.avatar_url,
required this.bio,
});
}
The generated class gives you the fields and constructor. In production you need serialization methods. Add them manually:
class UserProfile {
final String id;
final String displayName;
final String email;
final int followerCount;
final bool isVerified;
final String avatarUrl;
final String? bio; // nullable — use String? not dynamic
const UserProfile({
required this.id,
required this.displayName,
required this.email,
required this.followerCount,
required this.isVerified,
required this.avatarUrl,
this.bio, // optional named param (no required)
});
factory UserProfile.fromJson(Map<String, dynamic> json) {
return UserProfile(
id: json['id'] as String,
displayName: json['display_name'] as String,
email: json['email'] as String,
followerCount: (json['follower_count'] as num).toInt(),
isVerified: json['is_verified'] as bool,
avatarUrl: json['avatar_url'] as String,
bio: json['bio'] as String?,
);
}
Map<String, dynamic> toJson() => {
'id': id,
'display_name': displayName,
'email': email,
'follower_count': followerCount,
'is_verified': isVerified,
'avatar_url': avatarUrl,
if (bio != null) 'bio': bio,
};
}
The generator uses dynamic for null fields in JSON. With Dart's sound null safety (enabled by default since Dart 2.12), replace dynamic with the actual nullable type:
// Generated (unsafe)
final dynamic bio;
// Correct with null safety
final String? bio; // nullable String
final int? age; // nullable int
final List<String>? tags; // nullable list
// Also: integer fields come out as double
final double follower_count; // generated
// Fix: use int for count/quantity fields
final int followerCount; // corrected
For larger projects, the freezed package generates fromJson/toJson, copyWith, equality, and pattern matching automatically:
// pubspec.yaml
dependencies:
freezed_annotation: ^2.4.0
json_annotation: ^4.8.0
dev_dependencies:
freezed: ^2.4.0
json_serializable: ^6.7.0
build_runner: ^2.4.0
// user_profile.dart
import 'package:freezed_annotation/freezed_annotation.dart';
part 'user_profile.freezed.dart';
part 'user_profile.g.dart';
@freezed
class UserProfile with _$UserProfile {
const factory UserProfile({
required String id,
@JsonKey(name: 'display_name') required String displayName,
required String email,
@JsonKey(name: 'follower_count') required int followerCount,
@JsonKey(name: 'is_verified') required bool isVerified,
@JsonKey(name: 'avatar_url') required String avatarUrl,
String? bio,
}) = _UserProfile;
factory UserProfile.fromJson(Map<String, dynamic> json) =>
_$UserProfileFromJson(json);
}
// Usage
final user = UserProfile.fromJson(apiResponse);
final updated = user.copyWith(bio: 'New bio text');
Run dart run build_runner build to generate the implementation files. The generated code handles fromJson, toJson, ==, hashCode, and copyWith automatically.
Why does the generator use double for integers? JSON numbers are untyped — 1840 and 18.40 look the same to the parser. The generator uses double as a safe default. Cast to int for count and ID fields: (json['count'] as num).toInt().
Does it handle nested objects? Yes — nested JSON objects become separate Dart classes. Add a fromJson factory to each nested class and call it in the parent's factory: address: Address.fromJson(json['address'] as Map<String, dynamic>).
Should I use this or json_serializable from the start? For small projects, hand-written fromJson/toJson is fine. For anything with 10+ models, json_serializable or freezed saves significant time and avoids manual errors.
Is my JSON sent to a server? No. TypeMorph runs entirely in your browser — none of your API response data leaves your machine.
Is the processing local-only?
Absolutely. TypeMorph operates entirely within your browser's sandbox. We use Web Workers for high-performance computation without ever transmitting your JSON, SQL, or API data to a remote server.
Can I use this for enterprise projects?
Yes. The tool is designed for professional software engineers who require GDPR compliance and data privacy. It is trusted by developers at top-tier startups and financial institutions.