Menu
CoddyTech

Un constructor fromJson

Parte de la sección Datos y red del Journey de Flutter de Coddy. Lección 17 de 27.

Leer user['name'] as String por todas partes en las pantallas se vuelve engorroso, y un solo error tipográfico en una clave lo rompe. La solución habitual es crear una clase para los datos, con un constructor que conoce el JSON. Se llama fromJson:

class User {
  User({required this.name, required this.age});

  final String name;
  final int age;

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

User.fromJson es un constructor con nombre, una segunda forma de crear un User. La palabra factory permite que un constructor ejecute código y return el objeto que crea. Un constructor común no puede return nada.

Ahora el JSON se lee en un solo lugar. El resto de la aplicación obtiene un User real, donde user.name es un String y un error tipográfico provoca un error de compilación en lugar de un null:

final body = await fetch('/user');
return User.fromJson(jsonDecode(body));

Casi todas las aplicaciones que se comunican con un servidor tienen un fromJson para cada tipo de datos que reciben.

challenge icon

Desafío

Difícil

El perfil indica Unknown: loadUser ignora el JSON. Añade a User una factory User.fromJson(Map<String, dynamic> json) que lea name, email y age, y haz que loadUser devuelva User.fromJson(jsonDecode(body)). Importa dart:convert.

Pruébalo tú mismo

import 'package:flutter/material.dart';

import 'api.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: ProfileScreen(),
    );
  }
}

class ProfileScreen extends StatefulWidget {
  const ProfileScreen({super.key});

  @override
  State<ProfileScreen> createState() => _ProfileScreenState();
}

class _ProfileScreenState extends State<ProfileScreen> {
  late Future<User> _user;

  @override
  void initState() {
    super.initState();
    _user = loadUser();
  }

  Future<User> loadUser() async {
    final body = await fetch('/user');
    print(body);
    return User(name: 'Unknown', email: 'unknown', age: 0);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Profile')),
      body: Center(
        child: FutureBuilder<User>(
          future: _user,
          builder: (context, snapshot) {
            if (snapshot.hasError) {
              return Text('Could not load: ${snapshot.error}');
            }
            if (snapshot.hasData) {
              final user = snapshot.data!;
              return Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text(
                    user.name,
                    style: const TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
                  ),
                  const SizedBox(height: 8),
                  Text(user.email, style: const TextStyle(fontSize: 18)),
                  const SizedBox(height: 8),
                  Text('${user.age} years old'),
                ],
              );
            }
            return const CircularProgressIndicator();
          },
        ),
      ),
    );
  }
}

class User {
  User({required this.name, required this.email, required this.age});

  final String name;
  final String email;
  final int age;
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Datos y red

Practica por tu cuenta: Compilador de Flutter online