Arquitectura limpia de temas: archivo MiTema, estilos globales para botones y textos, y soporte para Modo Oscuro
La arquitectura más limpia, profesional y escalable en Flutter para la gestión de estilos consiste en centralizar la configuración visual en un archivo de tema dedicado (lib/themes/mi_tema.dart) y conectarlo directamente a MaterialApp en main.dart.
Este enfoque permite definir estilos globales para botones, tipografías y contenedores de una sola vez, además de proporcionar soporte integrado para modo claro y modo oscuro respetando las preferencias del sistema.
1. El archivo de configuración de tema: lib/themes/mi_tema.dart
Sección titulada «1. El archivo de configuración de tema: lib/themes/mi_tema.dart»import 'package:flutter/material.dart'; // Importa la librería base de componentes y temas de Materialimport 'paleta.dart'; // Importa la paleta de colores corporativa centralizada
class MiTema { // Clase estática para la gestión y configuración centralizada de temas static ThemeData get lightTheme { // Getter que construye y devuelve el tema claro return ThemeData( // Objeto principal de configuración de tema de Flutter useMaterial3: true, // Activa las directrices y comportamientos modernos de Material 3 colorScheme: ColorScheme.fromSeed( // Genera la paleta armónica a partir del color base seedColor: Paleta.primario, // Color semilla corporativo definido en la paleta brightness: Brightness.light, // Establece la luminosidad clara ), // Fin de ColorScheme.fromSeed scaffoldBackgroundColor: Paleta.fondo, // Color de fondo predeterminado para las pantallas
elevatedButtonTheme: ElevatedButtonThemeData( // Estilo homogéneo para todos los botones elevados style: ElevatedButton.styleFrom( // Define las propiedades visuales compartidas del botón backgroundColor: Paleta.primario, // Fondo azul corporativo por defecto foregroundColor: Colors.white, // Texto e iconos en color blanco elevation: 2, // Sombra sutil de elevación padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), // Relleno interior uniforme shape: RoundedRectangleBorder( // Geometría de bordes del botón borderRadius: BorderRadius.circular(8), // Curvatura de bordes redondeados ), // Fin de RoundedRectangleBorder ), // Fin de ElevatedButton.styleFrom ), // Fin de ElevatedButtonThemeData
textTheme: const TextTheme( // Jerarquía tipográfica global de la app headlineMedium: TextStyle( // Estilo para encabezados medianos fontSize: 22, // Tamaño de fuente en píxeles lógicos fontWeight: FontWeight.bold, // Grosor tipográfico en negrita color: Paleta.textoPrincipal, // Color de texto principal de alto contraste ), // Fin de TextStyle bodyMedium: TextStyle( // Estilo para textos de cuerpo y párrafos fontSize: 15, // Tamaño de fuente base de lectura color: Paleta.textoSecundario, // Color atenuado para texto secundario ), // Fin de TextStyle ), // Fin de TextTheme ); // Fin de ThemeData } // Fin de lightTheme
static ThemeData get darkTheme { // Getter que construye y devuelve el tema oscuro return ThemeData( // Objeto de configuración de tema para modo noche useMaterial3: true, // Aplica Material 3 en modo oscuro colorScheme: ColorScheme.fromSeed( // Genera la paleta tonal oscura seedColor: Paleta.primario, // Color semilla base brightness: Brightness.dark, // Establece la luminosidad oscura ), // Fin de ColorScheme.fromSeed scaffoldBackgroundColor: const Color(0xFF0F172A), // Fondo pizarra oscuro para evitar fatiga visual
elevatedButtonTheme: ElevatedButtonThemeData( // Estilo adaptado para botones en modo oscuro style: ElevatedButton.styleFrom( // Define propiedades del botón backgroundColor: Paleta.secundario, // Tono secundario visible sobre fondo oscuro foregroundColor: Colors.white, // Texto blanco de alto contraste shape: RoundedRectangleBorder( // Geometría de bordes borderRadius: BorderRadius.circular(8), // Esquinas redondeadas ), // Fin de RoundedRectangleBorder ), // Fin de ElevatedButton.styleFrom ), // Fin de ElevatedButtonThemeData ); // Fin de ThemeData } // Fin de darkTheme} // Fin de la clase MiTema2. Aplicación limpia en main.dart con soporte automático de Modo Oscuro
Sección titulada «2. Aplicación limpia en main.dart con soporte automático de Modo Oscuro»En el archivo main.dart, inyectamos el tema claro, el tema oscuro y le indicamos a Flutter que siga automáticamente las preferencias del sistema operativo del usuario:
import 'package:flutter/material.dart'; // Importa la librería Materialimport 'themes/mi_tema.dart'; // Importa la definición centralizada de temas claro y oscuroimport 'pantalla_principal.dart'; // Importa la pantalla principal
void main() { // Punto de arranque oficial del programa runApp(const MainApp()); // Arranca la app con el widget de configuración} // Fin de la función main
class MainApp extends StatelessWidget { // Widget raíz inmutable que configura el MaterialApp const MainApp({super.key}); // Constructor constante
@override // Sobreescribe el método build Widget build(BuildContext context) { // Retorna la estructura principal return MaterialApp( // Contenedor global de la aplicación title: 'Mi App con Temas', // Título de la app theme: MiTema.lightTheme, // Asigna el tema que se activará en modo claro darkTheme: MiTema.darkTheme, // Asigna el tema que se activará en modo oscuro themeMode: ThemeMode.system, // Alterna automáticamente según las preferencias del sistema home: const PantallaPrincipal(), // Pantalla de inicio de la app ); // Fin de MaterialApp } // Fin del método build} // Fin de la clase MainApp3. Cómo sobrescribir el estilo de un botón puntual
Sección titulada «3. Cómo sobrescribir el estilo de un botón puntual»Gracias a esta arquitectura centralizada, todos los botones de la aplicación heredan automáticamente el estilo, espaciado y bordes predeterminados. Si en una pantalla puntual se requiere un botón con una variante específica (por ejemplo, un botón verde de confirmación o un botón rojo de cancelación), basta con sobrescribir las propiedades deseadas con .styleFrom(...):
ElevatedButton( // Botón con personalización puntual de estilo style: ElevatedButton.styleFrom( // Sobreescribe únicamente los valores indicados backgroundColor: Paleta.botonVerde, // Aplica color de fondo verde específico para este botón foregroundColor: Paleta.textoBotonVerde, // Aplica color de texto blanco para este botón ), // Fin de ElevatedButton.styleFrom onPressed: () {}, // Callback al pulsar el botón child: const Text('Confirmar Operación'), // Etiqueta del botón) // Fin del widget ElevatedButton