flutter-refactorizacion-const-clean-code

Refactorización de widgets y uso de const en Flutter

  • 5 min

La refactorización es mejorar la estructura del código sin cambiar lo que hace.

Si has seguido el curso hasta aquí, es probable que tu archivo main.dart esté empezando a dar miedo. Tienes un Scaffold, dentro un SingleChildScrollView, dentro un Column, dentro 5 Container con BoxDecoration

A esto lo llamamos el “Infierno de los Paréntesis” o la Pyramid of Doom.

La indentación se va tanto a la derecha que necesitas un monitor panorámico para leer el código. Hoy vamos a aprender a limpiar este desastre aplicando Refactorización y, de paso, veremos por qué :key[const] ayuda al rendimiento.

¿Por qué Refactorizar?

Refactorizar no es cambiar lo que hace el código, es mejorar su estructura sin alterar su comportamiento.

En Flutter, dividir una pantalla compleja en widgets separados tiene tres ventajas:

  1. Legibilidad: Pasas de leer 500 líneas a leer 50.
  2. Reutilización: Si creas un widget “TarjetaProducto”, puedes usarlo en el Home, en el Carrito y en el Buscador.
  3. Aislamiento: Cada widget tiene una responsabilidad y puede reconstruirse desde el punto adecuado del árbol.

Extraer widgets

Imagina que tienes este botón personalizado repetido 3 veces en tu código:

Container(
  padding: EdgeInsets.all(10),
  decoration: BoxDecoration(color: Colors.blue, borderRadius: BorderRadius.circular(8)),
  child: Text("Púlsame", style: TextStyle(color: Colors.white)),
)
Copied!

Hay dos formas habituales de extraerlo: una función auxiliar o una clase de widget.

Función auxiliar

Una función que devuelve un widget puede ser suficiente para un fragmento pequeño y local.

Widget _construirBoton() {
  return Container(...);
}
Copied!

¿Por qué es malo? Porque cada vez que hagas setState en la pantalla principal, Flutter volverá a ejecutar esta función y creará el Widget de cero, desperdiciando ciclos de CPU.

Clase de widget

Cuando el fragmento tiene identidad, parámetros o lógica propia, conviene crear una clase de widget. Así obtiene su propio contexto, puede reutilizarse y forma un subárbol independiente.

Afortunadamente, VS Code lo hace por ti.

Pon el cursor sobre el Widget que quieres extraer (Container en el ejemplo).

Pulsa Ctrl + . (Windows) o Cmd + . (Mac).

Selecciona “Extract Widget”.

Escribe el nombre, por ejemplo BotonPersonalizado.

VS Code generará esto automáticamente al final de tu archivo:

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

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(10),
      decoration: BoxDecoration(color: Colors.blue, borderRadius: BorderRadius.circular(8)),
      child: Text("Púlsame", style: TextStyle(color: Colors.white)),
    );
  }
}
Copied!

Y en tu código principal, ahora solo tendrás:

Column(
  children: [
    BotonPersonalizado(),
    BotonPersonalizado(),
  ],
)
Copied!

¡Mucho más limpio!

Parametrizar el widget

Al extraer el widget, a menudo querrás que sea dinámico (cambiar el texto o el color). Simplemente añadimos propiedades final al constructor, como aprendimos en el módulo de Dart.

class BotonPersonalizado extends StatelessWidget {
  final String texto;
  final VoidCallback onPressed; // Tipo especial para funciones sin argumentos

  const BotonPersonalizado({
    required this.texto,
    required this.onPressed,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onPressed, // Usamos la función que nos pasan
      child: Container(
        // ... diseño ...
        child: Text(texto),
      ),
    );
  }
}
Copied!

Organizando archivos

Si el widget se reutiliza o el archivo ha crecido demasiado, muévelo fuera de main.dart.

  1. Crea una carpeta /lib/widgets.
  2. Crea un archivo boton_personalizado.dart.
  3. Corta y pega la clase allí.
  4. Importa package:flutter/material.dart al principio del nuevo archivo.
  5. En tu main.dart, importa el nuevo archivo.

No hace falta crear un archivo por cada widget pequeño: agrupa los que tengan una responsabilidad común y separa los que se reutilicen o evolucionen por su cuenta.

const y rendimiento

Aquí llegamos al punto técnico clave. Verás que el linter de Dart te sugiere constantemente: “Add ‘const’ modifier”.

¿Por qué es tan pesado con el const?

Cuando haces setState, Flutter marca ese elemento para reconstrucción y vuelve a ejecutar su método build.

Sin embargo, si usas const delante de un constructor:

const Icon(Icons.star)
Copied!

Le estás diciendo a Flutter: “Esta configuración es constante. Si vuelve a aparecer en la misma posición, puedes reutilizar la misma instancia y evitar actualizar ese subárbol”.

El impacto real

const puede evitar trabajo durante una reconstrucción, pero no convierte por sí solo una interfaz lenta en rápida. El coste también depende del tamaño del subárbol, del layout, de la pintura y de las operaciones que realice el código.

Como criterio práctico:

  • Si un widget y todos sus argumentos se conocen en compilación, ponle const.
  • VS Code es tu amigo: activa la opción “fix all” al guardar para que añada los const automáticamente.

Ejemplo de refactorización

Vamos a ver un “Antes y Después” rápido.

Antes (Sucio):

ListView(
  children: [
    Container(
      child: Row(children: [Icon(Icons.person), Text("Juan")]),
    ),
    Container(
      child: Row(children: [Icon(Icons.person), Text("Ana")]),
    ),
  ],
)
Copied!

Después (Limpio y Optimizado):

ListView(
  children: [
    const ItemUsuario(nombre: "Juan"), // Usamos const porque "Juan" es fijo aquí
    const ItemUsuario(nombre: "Ana"),
  ],
)

// En otro archivo:
class ItemUsuario extends StatelessWidget {
  final String nombre;
  const ItemUsuario({required this.nombre}); 

  @override
  Widget build(BuildContext context) {
    return Container(
       child: Row(children: [const Icon(Icons.person), Text(nombre)]),
    );
  }
}
Copied!