DevToolBoxGRATIS
Blog

JSON a Dart: Guía completa de clases modelo Flutter

10 min de lecturapor DevToolBox

Convertir JSON a clases modelo Dart es una de las tareas más comunes en el desarrollo Flutter.

Por qué necesitas clases modelo

Mapeo básico de tipos JSON a Dart

Tipo JSONTipo DartEjemplo
stringString"hello" → "hello"
number (int)int42 → 42
number (float)double3.14 → 3.14
booleanbooltrue → true
nullNull / dynamicnull → null
arrayList<T>[1,2,3] → [1,2,3]
objectMap / Class{} → User()

Clase modelo manual (fromJson / toJson)

El enfoque más directo es escribir las clases a mano:

// JSON input
{
  "id": 1,
  "name": "John Doe",
  "email": "john@example.com",
  "isActive": true,
  "score": 95.5
}

// Dart model class
class User {
  final int id;
  final String name;
  final String email;
  final bool isActive;
  final double score;

  User({
    required this.id,
    required this.name,
    required this.email,
    required this.isActive,
    required this.score,
  });

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'] as int,
      name: json['name'] as String,
      email: json['email'] as String,
      isActive: json['isActive'] as bool,
      score: (json['score'] as num).toDouble(),
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'name': name,
      'email': email,
      'isActive': isActive,
      'score': score,
    };
  }
}

Manejo de objetos anidados

Crea clases Dart separadas para cada nivel:

// JSON with nested object
{
  "id": 1,
  "name": "John",
  "address": {
    "street": "123 Main St",
    "city": "Springfield",
    "zipCode": "62701"
  }
}

// Dart classes
class Address {
  final String street;
  final String city;
  final String zipCode;

  Address({required this.street, required this.city, required this.zipCode});

  factory Address.fromJson(Map<String, dynamic> json) {
    return Address(
      street: json['street'] as String,
      city: json['city'] as String,
      zipCode: json['zipCode'] as String,
    );
  }

  Map<String, dynamic> toJson() => {
    'street': street, 'city': city, 'zipCode': zipCode,
  };
}

class User {
  final int id;
  final String name;
  final Address address;

  User({required this.id, required this.name, required this.address});

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'] as int,
      name: json['name'] as String,
      address: Address.fromJson(json['address'] as Map<String, dynamic>),
    );
  }

  Map<String, dynamic> toJson() => {
    'id': id, 'name': name, 'address': address.toJson(),
  };
}

Manejo de listas y arrays

Los arrays JSON se mapean a tipos List de Dart:

// JSON with arrays
{
  "name": "John",
  "hobbies": ["coding", "reading", "gaming"],
  "orders": [
    {"id": 1, "product": "Laptop", "price": 999.99},
    {"id": 2, "product": "Mouse", "price": 29.99}
  ]
}

// Dart
class User {
  final String name;
  final List<String> hobbies;
  final List<Order> orders;

  User({required this.name, required this.hobbies, required this.orders});

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      name: json['name'] as String,
      hobbies: List<String>.from(json['hobbies'] as List),
      orders: (json['orders'] as List)
          .map((e) => Order.fromJson(e as Map<String, dynamic>))
          .toList(),
    );
  }
}

Mejores prácticas de Null Safety

Con el null safety de Dart, maneja correctamente los campos nullable:

  • Usa `required` para campos obligatorios
  • Usa `?` para tipos nullable
  • Proporciona valores por defecto con `??`
  • Usa `late` solo cuando la inicialización es segura
class User {
  final int id;
  final String name;
  final String? bio;        // nullable
  final String avatarUrl;   // with default

  User({
    required this.id,
    required this.name,
    this.bio,
    this.avatarUrl = 'https://example.com/default-avatar.png',
  });

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'] as int,
      name: json['name'] as String,
      bio: json['bio'] as String?,
      avatarUrl: json['avatarUrl'] as String? ?? 'https://example.com/default-avatar.png',
    );
  }
}

Usando json_serializable

Para proyectos grandes, json_serializable automatiza el código repetitivo:

Paso 1: Agregar dependencias

# pubspec.yaml
dependencies:
  json_annotation: ^4.8.1

dev_dependencies:
  json_serializable: ^6.7.1
  build_runner: ^2.4.6

Paso 2: Crear modelo con anotaciones

import 'package:json_annotation/json_annotation.dart';

part 'user.g.dart';

@JsonSerializable()
class User {
  final int id;
  final String name;
  final String email;

  @JsonKey(name: 'is_active')
  final bool isActive;

  @JsonKey(defaultValue: 0.0)
  final double score;

  User({
    required this.id,
    required this.name,
    required this.email,
    required this.isActive,
    required this.score,
  });

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

Paso 3: Ejecutar Build Runner

dart run build_runner build --delete-conflicting-outputs

Usando Freezed para modelos inmutables

El paquete freezed genera clases inmutables con copyWith y serialización JSON:

import 'package:freezed_annotation/freezed_annotation.dart';

part 'user.freezed.dart';
part 'user.g.dart';

@freezed
class User with _$User {
  const factory User({
    required int id,
    required String name,
    required String email,
    @Default(false) bool isActive,
  }) = _User;

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}

Errores comunes y soluciones

ErrorSolución
Usar `json['key']` sin verificar nullUsar `json['key'] as String? ?? ''`
Olvidar convertir objetos anidadosLlamar `NestedClass.fromJson()`
Usar `int` para todos los númerosUsar `num` y convertir
No manejar claves faltantesUsar `containsKey()` o valores por defecto
Retornar tipo incorrecto en toJsonAsegurar que objetos anidados llamen `.toJson()`

FAQ

¿Cuál es la mejor forma de convertir JSON a clases Dart?

Proyectos pequeños: conversión manual. Proyectos grandes: json_serializable o freezed.

¿Cómo manejo claves JSON dinámicas?

Usa Map<String, dynamic>.

¿json_serializable o freezed?

json_serializable para serialización simple, freezed para inmutabilidad.

¿Cómo manejo las fechas?

Usa DateTime.parse() para strings ISO 8601.

𝕏 Twitterin LinkedIn
¿Fue útil?

Mantente actualizado

Recibe consejos de desarrollo y nuevas herramientas.

Sin spam. Cancela cuando quieras.

Prueba estas herramientas relacionadas

DTJSON to Dart{ }JSON FormatterKTJSON to KotlinTSJSON to TypeScript

Artículos relacionados

Sintaxis YAML y validación: Errores comunes y cómo solucionarlos

Domina la sintaxis YAML: reglas de indentación, errores de parsing, tipos de datos y mejores prácticas.

JSON a Kotlin Data Class: Guia kotlinx.serialization, Moshi y Gson

Convierte JSON a data class Kotlin en linea. Aprende parsing JSON con kotlinx.serialization, Moshi y Gson.