En Flutter, todo es un Widget. Un Widget es un componente inmutable que describe parte de la interfaz de usuario. Los widgets son los bloques de construcción fundamentales que definen tanto la estructura como el estilo de tu aplicación.
Filosofía "Everything is a Widget"En Flutter, desde un simple texto hasta layouts complejos, todo se construye con widgets:
Inmutabilidad de WidgetsLos widgets son inmutables, lo que significa que una vez creados, no pueden cambiar. Para actualizar la UI, Flutter crea nuevos widgets y reemplaza los antiguos.
// Error Esto NO es posible - los widgets son inmutables
Text miTexto = Text('Hola');
miTexto.data = 'Nuevo texto'; // Error!
// Forma correcta - crear un nuevo widget
Text('Hola') // Widget original
Text('Nuevo texto') // Nuevo widget Widgets que no cambian durante su ciclo de vida. Son ideales para UI estática.
class MiWidgetEstatico extends StatelessWidget {
final String titulo;
const MiWidgetEstatico({Key? key, required this.titulo}) : super(key: key);
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(16),
child: Text(
titulo,
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
);
}
}
// Uso
MiWidgetEstatico(titulo: 'Texto que no cambia') Widgets que pueden cambiar su estado interno y reconstruir la UI.
class Contador extends StatefulWidget {
_ContadorState createState() => _ContadorState();
}
class _ContadorState extends State<Contador> {
int _contador = 0;
void _incrementar() {
setState(() {
_contador++;
});
}
Widget build(BuildContext context) {
return Column(
children: [
Text('Contador: $_contador'),
ElevatedButton(
onPressed: _incrementar,
child: Text('Incrementar'),
),
],
);
}
} Árbol de Widgets (Widget Tree)Flutter organiza los widgets en una estructura jerárquica llamada Widget Tree:
MaterialApp( // Raíz
home: Scaffold( // Estructura principal
appBar: AppBar( // Barra superior
title: Text('Mi App'), // Texto del título
),
body: Center( // Centrar contenido
child: Column( // Columna vertical
children: [
Text('¡Hola!'), // Primer elemento
ElevatedButton( // Segundo elemento
onPressed: () {},
child: Text('Botón'),
),
],
),
),
),
) Método build()El corazón de todo widget. Define cómo se ve el widget:
Widget build(BuildContext context) {
// Este método se llama cada vez que el widget necesita reconstruirse
return Container(
width: 200,
height: 100,
color: Colors.blue,
child: Center(
child: Text('Widget personalizado'),
),
);
} setState() - Actualización de EstadoMétodo para notificar a Flutter que el estado ha cambiado:
class MiWidget extends StatefulWidget {
_MiWidgetState createState() => _MiWidgetState();
}
class _MiWidgetState extends State<MiWidget> {
bool _visible = true;
void _toggleVisibilidad() {
setState(() { // ← Notifica que algo cambió
_visible = !_visible;
});
}
Widget build(BuildContext context) {
return Column(
children: [
if (_visible) Text('¡Soy visible!'),
ElevatedButton(
onPressed: _toggleVisibilidad,
child: Text(_visible ? 'Ocultar' : 'Mostrar'),
),
],
);
}
} El Text widget se utiliza para mostrar texto en la UI. Puedes personalizar su apariencia con estilos, como tamaño de fuente, color y peso.
// Text - Mostrar texto
Text(
'Hola Flutter',
style: TextStyle(
fontSize: 24,
color: Colors.blue,
fontWeight: FontWeight.bold,
),
)
// Image - Mostrar imágenes
Image.network('https://ejemplo.com/imagen.jpg')
Image.asset('assets/images/logo.png')
// Icon - Iconos predefinidos
Icon(
Icons.favorite,
color: Colors.red,
size: 30,
) // Container - Caja con estilo
Container(
width: 200,
height: 100,
padding: EdgeInsets.all(16),
margin: EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(12),
),
child: Text('Contenido'),
)
// Column - Organizar verticalmente
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Elemento 1'),
Text('Elemento 2'),
Text('Elemento 3'),
],
)
// Row - Organizar horizontalmente
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Icon(Icons.home),
Icon(Icons.search),
Icon(Icons.settings),
],
) Los widgets interactivos permiten a los usuarios interactuar con tu app. Ejemplos incluyen botones, campos de texto y switches.
// ElevatedButton - Botón elevado
ElevatedButton(
onPressed: () {
print('Botón presionado');
},
child: Text('Presionar'),
)
// TextField - Campo de texto
TextField(
decoration: InputDecoration(
labelText: 'Ingresa tu nombre',
border: OutlineInputBorder(),
),
onChanged: (valor) {
print('Texto: $valor');
},
)
// Switch - Interruptor
Switch(
value: true,
onChanged: (valor) {
print('Switch: $valor');
},
) class TarjetaUsuario extends StatelessWidget {
final String nombre;
final String email;
final String avatarUrl;
const TarjetaUsuario({
Key? key,
required this.nombre,
required this.email,
required this.avatarUrl,
}) : super(key: key);
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.all(16),
elevation: 4,
child: Padding(
padding: EdgeInsets.all(16),
child: Row(
children: [
// Avatar circular
CircleAvatar(
radius: 30,
backgroundImage: NetworkImage(avatarUrl),
),
SizedBox(width: 16),
// Información del usuario
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
nombre,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 4),
Text(
email,
style: TextStyle(
color: Colors.grey[600],
),
),
],
),
),
// Botón de acción
IconButton(
icon: Icon(Icons.more_vert),
onPressed: () {
print('Más opciones para $nombre');
},
),
],
),
),
);
}
}
// Uso
TarjetaUsuario(
nombre: 'Ana García',
email: 'ana@ejemplo.com',
avatarUrl: 'https://ejemplo.com/avatar.jpg',
) createState(): Crea el estado del widget initState(): Inicialización (se ejecuta una vez) build(): Construye la UI (se ejecuta múltiples veces) setState(): Notifica cambios de estado dispose(): Limpieza cuando el widget se destruye
class MiWidgetCompleto extends StatefulWidget {
_MiWidgetCompletoState createState() => _MiWidgetCompletoState();
}
class _MiWidgetCompletoState extends State<MiWidgetCompleto> {
String _mensaje = '';
void initState() {
super.initState();
_mensaje = 'Widget inicializado';
print('initState: Widget creado');
}
Widget build(BuildContext context) {
print('build: Construyendo UI');
return Column(
children: [
Text(_mensaje),
ElevatedButton(
onPressed: () {
setState(() {
_mensaje = 'Estado actualizado: ${DateTime.now()}';
});
},
child: Text('Actualizar'),
),
],
);
}
void dispose() {
print('dispose: Widget destruido');
super.dispose();
}
} Composición sobre HerenciaPrefiere combinar widgets simples en lugar de crear widgets complejos:
// ✅ Buena práctica - Composición
Widget construirBotonPersonalizado() {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(colors: [Colors.blue, Colors.purple]),
borderRadius: BorderRadius.circular(25),
),
child: ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.transparent),
onPressed: () {},
child: Text('Botón Gradiente'),
),
);
} Extracción de WidgetsDivide widgets grandes en componentes más pequeños:
// ✅ Widget extraído y reutilizable
class BotonAccion extends StatelessWidget {
final String texto;
final VoidCallback onPressed;
final Color color;
const BotonAccion({
Key? key,
required this.texto,
required this.onPressed,
this.color = Colors.blue,
}) : super(key: key);
Widget build(BuildContext context) {
return ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: color),
onPressed: onPressed,
child: Text(texto),
);
}
} Los widgets son la base fundamental de Flutter. Dominar los conceptos de StatelessWidget, StatefulWidget, build() y setState() es esencial para crear aplicaciones efectivas.