flutter-json-serializacion-modelos

Convertir JSON en modelos de Dart con fromJson

  • 4 min

Un modelo de datos es una clase que representa de forma segura la información que viene de una API.

En el artículo anterior logramos descargar datos de una API. Pero terminamos con una List<dynamic>.

Trabajar con dynamic es caminar por la cuerda floja sin red. Si escribes usuario['nambre'] en lugar de usuario['nombre'], el editor no te avisará, el compilador no te avisará, y tu App crasheará cuando el usuario la esté usando.

Para evitar esto, en Flutter transformamos esos JSONs genéricos en Clases de Dart con nombre y apellido. A este proceso lo llamamos Serialización y Deserialización.

El concepto

El flujo de datos en una App profesional es siempre el mismo:

  1. JSON (String): Lo que llega por el cable de internet.
  2. Map (Diccionario): Dart convierte el texto en estructura clave-valor (jsonDecode).
  3. Objeto (Instancia): Nosotros convertimos ese mapa en una clase User o Product.

De esta forma, en nuestra interfaz usaremos user.name. Si nos equivocamos al escribir, VS Code nos marcará la línea en rojo antes de compilar. ¡Seguridad ante todo!

Crear el modelo de datos

Supongamos que la API nos devuelve este JSON de un usuario:

{
  "id": 1,
  "name": "Luis Llamas",
  "email": "[email protected]"
}
Copied!

Vamos a crear una clase en Dart para representarlo. Crearemos un archivo en /lib/models/user_model.dart.

Propiedades

Definimos los campos como final (porque los datos que vienen del servidor no deberían cambiar por accidente).

class User {
  final int id;
  final String name;
  final String email;

  User({
    required this.id, 
    required this.name, 
    required this.email
  });
}
Copied!

Constructor factory fromJson

Aquí ocurre la Deserialización. Creamos un constructor con nombre (Factory) que recibe el Mapa y devuelve una instancia de User.

  // Recibe un Map<String, dynamic> y devuelve un User
  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'],
      name: json['name'],
      email: json['email'], // Ojo: Asegúrate que las claves coinciden con el JSON
    );
  }
Copied!

Usamos factory por convención porque el constructor puede decidir qué instancia devolver y no está obligado a crearla mediante un inicializador generativo. Para este ejemplo también podría usarse un constructor con nombre normal.

Método toJson

Si alguna vez necesitamos enviar este usuario de vuelta al servidor (en un POST), necesitamos el camino inverso: Serialización.

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

Generar modelos a partir de JSON

Escribir fromJson a mano para un usuario con 3 campos es fácil. Pero si tienes una respuesta con 50 campos, arrays anidados y objetos dentro de objetos, hacerlo a mano es una locura y fallarás seguro.

Para esto existen generadores de código. Mi favorito para empezar es QuickType.

Entra en app.quicktype.io.

  1. Pega tu JSON a la izquierda.
  2. Selecciona “Dart” a la derecha.
  3. ¡Copia el código generado y pégalo en tu archivo!

Te ahorrará horas de trabajo y evitará errores tipográficos.

Integrar el modelo en el servicio

Ahora volvemos a nuestro user_service.dart. Vamos a eliminar el rastro de dynamic y usar nuestro flamante modelo User.

import 'dart:convert';
import 'package:http/http.dart' as http;
import '../models/user_model.dart'; // Importamos el modelo

class UserService {
  final String _baseUrl = 'jsonplaceholder.typicode.com';

  // Fíjate: Ahora devolvemos una lista de USER, no de dynamic
  Future<List<User>> getUsers() async {
    final url = Uri.https(_baseUrl, '/users');
    final response = await http.get(url);

    if (response.statusCode == 200) {
      // 1. Decodificamos el String a una Lista de Mapas
      List<dynamic> listaMapas = jsonDecode(response.body);

      // 2. Mapeamos cada mapa a un objeto User
      // Recorremos la lista y convertimos cada item usando .fromJson
      List<User> usuarios = listaMapas
          .map((item) => User.fromJson(item))
          .toList();

      return usuarios;
    } else {
      throw Exception('Error al cargar');
    }
  }
}
Copied!

Desgranando el .map()

La línea .map((item) => User.fromJson(item)) es clave. Toma cada elemento “crudo” de la lista y lo pasa por la fábrica fromJson, devolviéndonos una lista de objetos limpios y seguros.

Usar el modelo en la UI

Finalmente, en nuestra pantalla, el cambio es pequeño pero muy práctico.

// En el ListView.builder
itemBuilder: (context, index) {
  // Ahora 'user' es de tipo User, no dynamic
  final user = usuarios[index]; 
  
  return ListTile(
    title: Text(user.name), // ✅ Autocompletado y sin errores
    subtitle: Text(user.email),
    // Text(user.nambre) // ❌ El editor marcaría ERROR aquí mismo
  );
}
Copied!