Guía de Inicio Rápido
🚀 Cómo usar la herramienta:
- Personaliza el nombre de la clase principal.
- Pega un objeto JSON (quoted o unquoted).
- Opcional: Usa Formatear para limpiar tu JSON.
- Configura el modo de salida (one-page / separated) en el panel.
⚡ Después de generar el código:
- Copia y pega el código en tú proyecto
- Ejecuta el generador de código de freezed:
flutter packages pub run build_runner build
Objetivo
El objetivo de esta herramienta es generar código a partir de una estructura de datos en formatoJSON. A partir del contenido proporcionado, el sistema produce modelos de datos inmutables con anotaciones freezed para proyectos desarrollados en Dart/Flutter. La herramienta realiza inferencia de tipos y construye la jerarquía de clases de manera automática, incluyendo el manejo de estructuras anidadas de forma recursiva.
Funcionalidades Clave
- Inferencia de Tipos Inteligente:
- Identificación automática de tipos primitivos:
int,double,boolyString. - Soporte para listas de tipos primitivos (por ejemplo,
List<String>). - Generación recursiva de submodelos para objetos anidados y colecciones de objetos (
List<T>).
- Identificación automática de tipos primitivos:
- Modos de Salida:Permite generar la salida en un solo archivo
one-pageo en múltiples archivos, uno por claseseparated, según la configuración seleccionada. - Manejo de Valores Nulos:Los campos con valores
nullse declaran como anulables (por ejemplo,String?) y se omite la anotaciónrequired. Además, se añade un comentario//! INFO: Verificar tipopara indicar la necesidad de revisión manual. - Formatos de Entrada:Acepta datos en formato JSON tanto con claves
quotedes decir con comillas, como sin comillasunquoted. En el caso de las claves sin comillas, el sistema las formatea automáticamente antes de la generación del código.
Ejemplo Simple
JSON de Entrada:
{"id": 1,"name": "Elias","profile": { "age": 27, "country": "Ecuador" }}
Salida Dart:
import 'package:freezed_annotation/freezed_annotation.dart';part 'user.freezed.dart';part 'user.g.dart';@freezedabstract class User with _$User {const factory User({required int id,required String name,required Profile profile,}) = _User;factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);}@freezedabstract class Profile with _$Profile {const factory Profile({required int age,required String country,}) = _Profile;factory Profile.fromJson(Map<String, dynamic> json) =>_$ProfileFromJson(json);}
Ejemplo Complejo (Recursividad)
JSON de Entrada (Anidado):
{"username": "tellxmaster","firstName": "Elias","lastName": "Elias","address": {"mainStreet": "Huigra","secondary": "Quichuas","reference": null,"coords": {"lat": 0.4,"long": 0,"geoDetails": {"altitude": 2800,"region": {"country": "Ecuador","province": "Chimborazo","city": "Alausí","neighborhood": {"name": "Centro Histórico","population": 3400,"security": {"level": "medium","patrols": ["night", "weekend"]}}}}}},"phone": {"mobile": "+593987654321","home": null,"verified": true},"social": {"email": "elias@example.com","links": {"github": "https://github.com/tellxmaster","twitter": "@tellxmaster","website": "https://tellxmaster.dev"}},"preferences": {"theme": "dark","language": "es","notifications": {"email": true,"sms": false,"push": {"enabled": true,"channels": ["system", "promotions", "security"]}}},"devices": [{"type": "laptop","brand": "Dell","os": {"name": "Ubuntu","version": "24.04","kernel": {"type": "Linux","build": "6.8.0-35-generic"}},"network": {"ip": "192.168.1.10","mac": "A4:B2: 1C: 3D: 9E:F2","connections": [{"ssid": "HomeWifi","strength": 85,"encryption": "WPA2"},{"ssid": "OfficeNet","strength": 70,"encryption": "WPA3"}]}},{"type": "smartphone","brand": "Google","model": "Pixel 8","os": {"name": "Android","version": "15","securityPatch": "2025-10-05"},"apps": [{"name": "Telegram","permissions": {"camera": true,"contacts": true,"location": false}},{"name": "GitHub","permissions": {"notifications": true,"storage": false}}]}],"activity": {"lastLogin": "2025-11-07T22: 45: 00Z","sessions": [{"device": "laptop","location": "Ecuador","ip": "192.168.1.10","timestamp": "2025-11-07T22: 40: 00Z"},{"device": "smartphone","location": "Ecuador","ip": "192.168.1.12","timestamp": "2025-11-06T19: 30: 00Z"}],"history": {"logins": 152,"actions": {"codePushes": 340,"issuesOpened": 58,"comments": 902,"repositories": {"active": 12,"archived": 5,"details": [{"name": "AI-Playground","language": "Python","contributors": [{"name": "María","role": "Maintainer"},{"name": "Elias","role": "Owner"}]}]}}}},"meta": {"createdAt": "2020-04-21T10: 00: 00Z","updatedAt": "2025-11-07T22: 45: 00Z","version": "2.5.1","tags": ["developer", "backend", "linux", "open-source"],"status": {"active": true,"verified": true,"roles": ["admin", "contributor"]}}}
Salida Dart (Clase principal):
import 'package:freezed_annotation/freezed_annotation.dart';part 'user_info.freezed.dart';part 'user_info.g.dart';@freezedabstract classUserInfo with _$UserInfo {const factory UserInfo({required String username,required String firstName,required String lastName,required Address address,required Phone phone,required Social social,required Preferences preferences,required Listdevices, required Activity activity,required Meta meta,}) = _UserInfo;factory UserInfo.fromJson(Map<String, dynamic> json) =>_$UserInfoFromJson(json);}@freezedabstract classAddress with _$Address {const factory Address({required String mainStreet,required String secondary,String? reference,required Coords coords,}) = _Address;factory Address.fromJson(Map<String, dynamic> json) =>_$AddressFromJson(json);}@freezedabstract classCoords with _$Coords {const factory Coords({required double lat,@JsonKey(name: 'long') required double lon,required GeoDetails geoDetails,}) = _Coords;factory Coords.fromJson(Map<String, dynamic> json) =>_$CoordsFromJson(json);}@freezedabstract classGeoDetails with _$GeoDetails {const factory GeoDetails({required int altitude,required Region region,}) = _GeoDetails;factory GeoDetails.fromJson(Map<String, dynamic> json) =>_$GeoDetailsFromJson(json);}@freezedabstract classRegion with _$Region {const factory Region({required String country,required String province,required String city,required Neighborhood neighborhood,}) = _Region;factory Region.fromJson(Map<String, dynamic> json) =>_$RegionFromJson(json);}@freezedabstract classNeighborhood with _$Neighborhood {const factory Neighborhood({required String name,required int population,required Security security,}) = _Neighborhood;factory Neighborhood.fromJson(Map<String, dynamic> json) =>_$NeighborhoodFromJson(json);}@freezedabstract classSecurity with _$Security {const factory Security({required String level,required List<String> patrols,}) = _Security;factory Security.fromJson(Map<String, dynamic> json) =>_$SecurityFromJson(json);}@freezedabstract classPhone with _$Phone {const factory Phone({String? mobile,String? home,required bool verified,}) = _Phone;factory Phone.fromJson(Map<String, dynamic> json) =>_$PhoneFromJson(json);}@freezedabstract classSocial with _$Social {const factory Social({required String email,required Links links,}) = _Social;factory Social.fromJson(Map<String, dynamic> json) =>_$SocialFromJson(json);}@freezedabstract classLinks with _$Links {const factory Links({String? github,String? twitter,String? website,}) = _Links;factory Links.fromJson(Map<String, dynamic> json) =>_$LinksFromJson(json);}@freezedabstract classPreferences with _$Preferences {const factory Preferences({required String theme,required String language,required Notifications notifications,}) = _Preferences;factory Preferences.fromJson(Map<String, dynamic> json) =>_$PreferencesFromJson(json);}@freezedabstract classNotifications with _$Notifications {const factory Notifications({required bool email,required bool sms,required Push push,}) = _Notifications;factory Notifications.fromJson(Map<String, dynamic> json) =>_$NotificationsFromJson(json);}@freezedabstract classPush with _$Push {const factory Push({required bool enabled,required List<String> channels,}) = _Push;factory Push.fromJson(Map<String, dynamic> json) =>_$PushFromJson(json);}@freezedabstract classDevice with _$Device {const factory Device({required String type,required String brand,String? model,required OperatingSystem os,Network? network,List? connections, List? apps, }) = _Device;factory Device.fromJson(Map<String, dynamic> json) =>_$DeviceFromJson(json);}@freezedabstract classOperatingSystem with _$OperatingSystem {const factory OperatingSystem({required String name,required String version,Kernel? kernel,String? securityPatch,}) = _OperatingSystem;factory OperatingSystem.fromJson(Map<String, dynamic> json) =>_$OperatingSystemFromJson(json);}@freezedabstract classKernel with _$Kernel {const factory Kernel({required String type,required String build,}) = _Kernel;factory Kernel.fromJson(Map<String, dynamic> json) =>_$KernelFromJson(json);}@freezedabstract classNetwork with _$Network {const factory Network({String? ip,String? mac,List? connections, }) = _Network;factory Network.fromJson(Map<String, dynamic> json) =>_$NetworkFromJson(json);}@freezedabstract classConnection with _$Connection {const factory Connection({required String ssid,required int strength,required String encryption,}) = _Connection;factory Connection.fromJson(Map<String, dynamic> json) =>_$ConnectionFromJson(json);}@freezedabstract classApp with _$App {const factory App({required String name,required AppPermissions permissions,}) = _App;factory App.fromJson(Map<String, dynamic> json) => _$AppFromJson(json);}@freezedabstract classAppPermissions with _$AppPermissions {const factory AppPermissions({bool? camera,bool? contacts,bool? location,bool? notifications,bool? storage,}) = _AppPermissions;factory AppPermissions.fromJson(Map<String, dynamic> json) =>_$AppPermissionsFromJson(json);}@freezedabstract classActivity with _$Activity {const factory Activity({required DateTime lastLogin,required Listsessions, required History history,}) = _Activity;factory Activity.fromJson(Map<String, dynamic> json) =>_$ActivityFromJson(json);}@freezedabstract classSession with _$Session {const factory Session({required String device,required String location,required String ip,required DateTime timestamp,}) = _Session;factory Session.fromJson(Map<String, dynamic> json) =>_$SessionFromJson(json);}@freezedabstract classHistory with _$History {const factory History({required int logins,required Actions actions,}) = _History;factory History.fromJson(Map<String, dynamic> json) =>_$HistoryFromJson(json);}@freezedabstract classActions with _$Actions {const factory Actions({required int codePushes,required int issuesOpened,required int comments,required Repositories repositories,}) = _Actions;factory Actions.fromJson(Map<String, dynamic> json) =>_$ActionsFromJson(json);}@freezedabstract classRepositories with _$Repositories {const factory Repositories({required int active,required int archived,required Listdetails, }) = _Repositories;factory Repositories.fromJson(Map<String, dynamic> json) =>_$RepositoriesFromJson(json);}@freezedabstract classRepoDetail with _$RepoDetail {const factory RepoDetail({required String name,required String language,required Listcontributors, }) = _RepoDetail;factory RepoDetail.fromJson(Map<String, dynamic> json) =>_$RepoDetailFromJson(json);}@freezedabstract classContributor with _$Contributor {const factory Contributor({required String name,required String role,}) = _Contributor;factory Contributor.fromJson(Map<String, dynamic> json) =>_$ContributorFromJson(json);}@freezedabstract classMeta with _$Meta {const factory Meta({required DateTime createdAt,required DateTime updatedAt,required String version,required List<String> tags,required Status status,}) = _Meta;factory Meta.fromJson(Map<String, dynamic> json) => _$MetaFromJson(json);}@freezedabstract classStatus with _$Status {const factory Status({required bool active,required bool verified,required List<String> roles,}) = _Status;factory Status.fromJson(Map<String, dynamic> json) =>_$StatusFromJson(json);}
Modos de Salida
Se puede configurar si todas las clases se generan en un solo archivo (one-page) o si cada modelo se separa en su propio archivo (`separated`), incluyendo las importaciones necesarias.
1. Modo one-page
// Contenido de 'user.dart'import 'package:freezed_annotation/freezed_annotation.dart';part 'user.freezed.dart';part 'user.g.dart';@freezedabstract class User with _$User {const factory User({required int id,required String name,required Profile profile,}) = _User;factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);}@freezedabstract class Profile with _$Profile {const factory Profile({required int age,required String country,}) = _Profile;factory Profile.fromJson(Map<String, dynamic> json) =>_$ProfileFromJson(json);}
2. Modo separated-page
// Archivo 1: user.dartimport 'package:freezed_annotation/freezed_annotation.dart';import 'profile.dart';part 'user.freezed.dart';part 'user.g.dart';@freezedclass User with _$User {const factory User({required int id,required String name,required Profile profile,}) = _User;factory User.fromJson(Map<String, dynamic> json) =>_$UserFromJson(json);}
// Archivo 2: profile.dartimport 'package:freezed_annotation/freezed_annotation.dart';part 'profile.freezed.dart';part 'profile.g.dart';@freezedclass Profile with _$Profile {const factory Profile({required int age,required String country,}) = _Profile;factory Profile.fromJson(Map<String, dynamic> json) =>_$ProfileFromJson(json);}
Manejo de Valores null
Cuando una propiedad tiene el valor null en el JSON, se infiere un tipo genérico anulable (String? por defecto), y se añade un comentario para la revisión manual del tipo exacto.
// Propiedad 'avatar' con valor: null en el JSON@freezedclass User with _$User {const factory User({// ...String? avatar, // //! INFO: Verificar tipo (ajustar si es int?, bool?, etc.)}) = _User;}
Formatos de Entrada JSON
1. JSON Quoted (Válido)
Se procesa directamente.
{"id": 1,"name": "Elias"}
2. JSON Unquoted (Claves sin comillas)
Se aplica un formateo automático para agregar las comillas a las claves, luego se genera el código.
{id: 1,name: "Elias", // Clave 'name' sin comillas}
3. Formato Inválido
Si un valor de tipo *string* no está entre comillas (ej. "name": Elias), el parser fallará.
{"id": 1,"name": Elias, // <- ERROR JSON inválido: string sin comillas}