DevToolBoxFREE
BlogAdvertise

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.

¿Fue útil?

Stay Updated

Get weekly dev tips and new tool announcements.

No spam. Unsubscribe anytime.

Partner Picks

Sponsor this article

Place your product next to this developer topic with tracked clicks.

Ask about article sponsorship

This site uses cookies for analytics and to display ads. By continuing to browse, you agree. Privacy Policy