Os StatefulWidget son aqueles widgets que poden cambiar co tempo, é dicir, teñen estado mutable. Son ideais para situacións nas que a interface debe actualizarse tras unha acción do usuario ou un evento.
❓ Cando usar StatefulWidget
Usamos StatefulWidget cando:
- A interface precisa cambiar despois de certa acción (por exemplo, ao premer un botón).
- Hai datos que cambian dinamicamente (como o contador dun cronómetro ou o contido dun formulario).
- A pantalla precisa gardar estado entre actualizacións (como o scroll nunha lista).
📌 Exemplos típicos:
- Contadores.
- Formularios que se validan mentres se escriben.
- Elementos interactivos como
Checkbox,Slider,Switch, etc.- Un campo de texto (
TextField) onde o usuario introduce información.- Un reprodutor de vídeo que mostra o tempo transcorrido.
🧬 Estrutura e ciclo de vida (initState, build, dispose)
A diferenza dos StatelessWidget que son unha única clase, un StatefulWidget componse de dúas clases:
- A clase principal, que estende de
StatefulWidget. É inmutable. - A clase
State:. Aquí é onde se gardan os datos que cambian e se constrúe a interface.
🔄 Métodos principais do ciclo de vida
| Método | Cando se chama | Para que serve |
|---|---|---|
initState() | Só unha vez, ao crear o widget | Inicializar variables, abrir recursos, comezar animacións |
build() | Sempre que se crea ou se chama setState() | Construír a interface visual do widget |
dispose() | Ao destruír o widget | Liberar recursos, pechar conexións ou controladores |
Ciclo de vida importante:
initState()
- Chamado unha vez ao comezar.
- Non é obrigatorio
- Úsase para inicializacións, como cargar datos ou realizar configuracións que só necesitan facerse unha vez.
- Chámase unha única vez cando o obxecto
Statese crea e se insire na árbore de widgets. - Sempre debes chamar a
super.initState()ao principio.
build(BuildContext context)
- Este método é obrigatorio e chámase cada vez que o widget necesita ser debuxado ou reconstruído (por exemplo, tras un
setState()). - Aquí definimos que widgets se deben mostrar.
dispose()
- Chamado cando o widget se elimina da árbore.
- Non é obrigatorio
- Úsase para liberar recursos, como cancelación de listeners ou controladores.
- Sempre debes chamar a
super.dispose()ao final
✍️ O método setState()
Cando o estado interno do teu widget cambia e queres que eses cambios se reflictan na pantalla, debes chamar a setState().
Chamando a
setState(), dígolle a Flutter que algo cambiou e debe reconstruír a interface.
setState(() {
// Actualizamos o valor dun atributo do estado
_contador++;
});
🧪 Exemplo práctico (Contador simple)
🧪 Outro contador
⚡ Xeración de Código Automático (Snippets)
Lembra que en Visual Studio Code dispomos do atallo (snippet) stful, de xeito que ao escribilo o editor automaticamente crea un StatefulWidget completo coa súa clase State asociada.

🧩 Comunicación entre o StatefulWidget e a súa clase State: propiedade widget
Cando traballamos cun StatefulWidget, debemos ter sempre presente unha idea fundamental:
- O
StatefulWidgeté inmutable → non se pode modificar unha vez creado. - A súa clase asociada,
State, é mutable → é quen garda e xestiona os datos que cambian.
Isto implica que:
- Os valores que lle pasamos polo construtor ao
StatefulWidgetnon poden alterarse. - Pero a interface da app si pode cambiar, e para iso usamos o estado (
State).
Para conectar ambos elementos, Flutter proporciona a propiedade especial chamada widget.
💡 Idea clave:
Os datos chegan polo widget (inmutable), pero é o estado quen os copia e os modifica. Esta separación é esencial para comprender o funcionamento deStatefulWidget.
🔍 A propiedade widget dentro do State
Dentro da clase State, existe unha propiedade chamada widget, que é unha referencia automática ao StatefulWidget pai.
Grazas a ela podemos acceder ás propiedades inmutables do widget desde o estado.
Exemplo:
class OMeuWidget extends StatefulWidget {
final String texto;
const OMeuWidget({super.key, required this.texto});
@override
State<OMeuWidget> createState() => _OMeuWidgetState();
}
class _OMeuWidgetState extends State<OMeuWidget> {
@override
Widget build(BuildContext context) {
return Text(widget.texto); // ← Acceso á propiedade do widget
}
}
🔄 Por que crear unha copia?
As propiedades dun StatefulWidget non se poden modificar directamente (son inmutables), pero moitas veces necesitamos traballar con elas:
- editalas,
- transformalas ou aplicarlle cambios
- ou reaccionar a cambios na UI.
Para iso creamos variables mutables dentro do estado, que si poden cambiar co setState().
🧠 Resumo:
| Elemento | Natureza | Función |
|---|---|---|
StatefulWidget | Inmutable | Recibe valores iniciais polo construtor |
State | Mutable | Garde e modifica datos durante a vida do widget |
widget | Referencia | Permite acceder ás propiedades do widget |
| Variables do estado | Mutables | Cambian a UI mediante setState() |
Exemplo:
class _OMeuWidgetState extends State<OMeuWidget> {
late String textoEditado;
@override
void initState() {
super.initState();
textoEditado = widget.texto; // Copia editable
}
}
Agora textoEditado pertence ao estado, e podemos modificalo libremente co setState().
🔁 Pasar datos modificables a un StatefulWidget (e recibir cambios)
É moi habitual que unha pantalla lle pase datos a outra para editalos, e despois, ao volver á pantalla anterior, queiramos recuperar os cambios.
Para iso existen dúas técnicas principais:
✔️ Usando
Navigator.push()para enviar datos eNavigator.pop(valor)para devolvelos.✔️ Usando callbacks, onde a pantalla pai lle pasa unha función ao fillo para que lle devolva os cambios inmediatamente.
📌 Recomendación: Antes de avanzar, asegúrate de ler o apartado dedicado aos callbacks, ao final deste tema, xa que será esencial para entender a segunda técnica.

Pasar datos mediante Navigator.push()
O proceso consta de 4 pasos fundamentais:
- 1️⃣ O
StatefulWidgetrecibe datos (inmutables) - 2️⃣ A clase
Statecopia os datos e modifícaos - 3️⃣ Devolver o valor desde a segunda pantalla
- 4️⃣ A pantalla anterior usa
await Navigator.pushe recibe datos de volta
1️⃣ O StatefulWidget recibe datos (inmutables)
Un StatefulWidget sempre recibe os datos a través do seu construtor.
Eses datos quedan gardados como propiedades final, porque o widget é inmutable.
No exemplo, a pantalla recibe un número enteiro chamado contador:
class PantallaContador extends StatefulWidget {
final int contador;
const PantallaContador({super.key, required this.contador});
@override
State<PantallaContador> createState() => _PantallaContadorState();
}
2️⃣ A clase State copia os datos e modifícaos
Unha vez que o StatefulWidget recibe os datos iniciais, é a clase State quen debe traballar con eles.
Lembra que as propiedades do widget son inmutables, polo que non poden modificarse directamente.
A solución consiste en crear unha copia editable dentro do estado:
- Declaramos unha variable mutable (Neste exemplo,
_contador). - Inicializámola en
initState()tomando o valor recibido polo widget. - A partir deste momento, os cambios faranse sempre sobre esta copia sando
setState().
class _PantallaContadorState extends State<PantallaContador> {
late int _contador;
@override
void initState() {
super.initState();
_contador = widget.contador; // Copia editable
}
}
Neste punto:
widget.contador→ valor inicial, inmutable_contador→ copia editable que pertence ao estado
3️⃣ Devolver o valor desde a segunda pantalla
Cando estamos nun StatefulWidget que actúa como editor (por exemplo, unha pantalla onde o usuario modifica datos), é habitual que, ao rematar, queiramos volver á pantalla anterior e devolvendo os datos actualizados.
Flutter permite isto mediante Navigator.pop().
Esta función non só pecha a pantalla actual, senón que pode devolver valores á pantalla que fixo o Navigator.push().
O proceso é o seguinte:
- O usuario preme un botón para gardar os cambios.
- Chamamos a
Navigator.pop(context, valor). - Ese valor é recibido pola pantalla anterior, que estará agardando cun
await Navigator.push(...), tal como veremos no seguinte punto.
Así, no exemplo:
Navigator.pop(context, _contador);
4️⃣ A pantalla anterior usa await Navigator.push e recibe datos de volta
Para completar o ciclo de comunicación entre pantallas, a pantalla orixinal —a que chama ao editor— debe agardar a que a segunda pantalla devolva un valor.
Isto faise mediante await Navigator.push(...), que pausa a execución ata que a pantalla destino chame a Navigator.pop(...).
O fluxo é o seguinte:
1. A pantalla principal chama ao editor usando Navigator.push.
2. A execución detense nese punto grazas a await.
3. Cando o usuario remata na pantalla secundaria, esta usa Navigator.pop(context, valor) para devolver un dato (tal e como vimos no apartado anterior).
4. O await recibe os datos, no exemplo recibimos o valor na variable novoContador.
5. Por último, actualizamos a interface chamando a setState().
Este proceso permite que a primeira pantalla se reconstrúa automaticamente co novo dato recibido.
Exemplo aplicado ao noso contador:
...
Future<void> _abrirEditor() async {
final novoContador = await Navigator.push<int>(
context,
MaterialPageRoute(
builder: (context) => PantallaContador(contador: _contador),
),
);
if (novoContador != null) {
setState(() {
_contador = novoContador;
});
}
}
@override
Widget build(BuildContext context) {
...
ElevatedButton(
onPressed: _abrirEditor,
child: const Text("Ir á pantalla para incrementar o contador"));
...
Neste punto:
Navigator.push→ abre a segunda pantallaawait→ queda esperando un resultadoNavigator.pop(_, valor)(na pantalla secundaria) → envía o valor de voltasetState()→ actualiza a UI coa nova información
📝 Exemplo completo:

🧠 Resumo conceptual
| Elemento | Natureza | Para que serve |
|---|---|---|
| StatefulWidget | Inmutable | Recibe os datos iniciais polo construtor. Non cambia durante a execución. |
| State | Mutable | Garde e modifica os datos que cambian coa interacción do usuario. |
| widget | Referencia ao StatefulWidget | Permite acceder ás propiedades inmutables que chegaron polo widget. |
| Variables do Estado | Mutables | Copias editables dos datos, modificables mediante setState(). |
| Navigator.push() | Navegación | Abre unha nova pantalla e pode devolver un valor á anterior. |
| Navigator.pop(valor) | Pechar + devolver datos | Pecha a pantalla actual e devolve valor á pantalla chamante. |
📌 Idea clave:
- O
StatefulWidgetrecibe datos (non se modifican).- O
Statecopia e modifica (si pode cambiar).- A actualización da UI sempre vai con
setState().- A pantalla anterior pode recibir datos mediante Navigator.