📋 Unha lista que pode cambiar
No Tema 3.2 aprendemos a mostrar listas utilizando ListView e ListView.builder.
Ata agora, os elementos das listas estaban definidos no código e non podiamos engadir ou eliminar elementos desde a interface.
Neste exemplo imos crear unha lista de alumnos que permita:
- Mostrar os alumnos que temos gardados.
- Engadir un alumno escribindo o seu nome.
- Eliminar un alumno premendo unha icona.
Para conseguilo, gardaremos os nomes nunha lista dentro do State e utilizaremos setState() cada vez que a modifiquemos.

📝 Engadir e eliminar elementos dunha lista
En Dart podemos crear unha lista de nomes:
final _nomes = <String>['Xurxo', 'Iria'];
Neste caso, a lista contén dous alumnos: Xurxo e Iria.
Para engadir un novo elemento, utilizamos o método add():
_nomes.add('Roi');
Agora a lista contén tres alumnos: Xurxo, Iria e Roi.
Para eliminar un elemento, podemos utilizar removeAt(), indicando a súa posición:
_nomes.removeAt(1);
Esta instrución elimina o elemento da posición 1, é dicir, Iria.
Lembra que as posicións dunha lista comezan en 0:
| Posición (índice) | Nome |
|---|---|
| 0 | Xurxo |
| 1 | Iria |
| 2 | Roi |
Tamén podemos consultar cantos elementos ten unha lista mediante length:
_nomes.length
E comprobar se está baleira mediante isEmpty:
_nomes.isEmpty
💡 A palabra
finalimpide substituír_nomespor outra lista, pero permite engadir ou eliminar elementos da lista existente.
➕ Engadir un alumno
Para engadir alumnos utilizaremos un TextField e un botón «Engadir».
O usuario escribirá un nome e, ao premer o botón, executarase o método _engadir():
void _engadir() {
final nome = _controlador.text.trim();
if (nome.isEmpty) return;
setState(() {
_nomes.add(nome);
});
_controlador.clear();
}
O funcionamento é o seguinte:
- Lemos o nome escrito no
TextFieldmediante_controlador.text. - Eliminamos os espazos dos extremos utilizando
trim(). - Comprobamos se está baleiro con
isEmpty. Se é así,returnremata o método e non engadimos nada. - Engadimos o nome á lista mediante
_nomes.add(nome). - Actualizamos a pantalla utilizando
setState(). - Limpamos o campo mediante
_controlador.clear()para poder escribir outro nome.
🔄 Por que necesitamos setState()?
Como vimos no Tema 3.3, setState() informa a Flutter de que cambiaron os datos que utiliza a interface e que debe reconstruíla.
Neste caso:
setState(() {
_nomes.add(nome);
});
A instrución add() engade o novo alumno á lista, mentres que setState() permite que Flutter actualice a pantalla para mostrar o cambio.
É importante distinguir as dúas operacións: add() modifica os datos e setState() provoca a actualización da interface.
➖ Eliminar un alumno
Para eliminar un alumno necesitamos coñecer a súa posición na lista.
Por exemplo, podemos crear este método:
void _eliminar(int indice) {
setState(() {
_nomes.removeAt(indice);
});
}
O parámetro indice indica a posición do alumno que queremos eliminar.
Se chamamos ao método con _eliminar(0), eliminaremos o primeiro alumno da lista. Se utilizamos _eliminar(1), eliminaremos o segundo.
Grazas a setState(), a pantalla actualizarase automaticamente para mostrar a lista sen ese alumno.
Con todo, non é recomendable eliminar un alumno inmediatamente ao premer a icona. Podemos evitar eliminacións accidentais preguntando primeiro ao usuario se realmente quere facelo.
💬 Confirmar a eliminación
Para pedir unha confirmación podemos utilizar un AlertDialog, unha pequena ventá que aparece sobre a pantalla e permite mostrar unha mensaxe con varios botóns.
Neste exemplo, cando o usuario prema a icona de eliminar, aparecerá unha pregunta con dúas opcións:
- Cancelar: pecha o diálogo sen eliminar o alumno.
- Eliminar: confirma que queremos eliminar o alumno.

🪟 Como mostramos un diálogo?
Para abrir un diálogo utilizamos showDialog().
Por exemplo:
showDialog(
context: context,
builder: (contextoDialogo) => AlertDialog(
title: const Text('Eliminar alumno?'),
content: const Text('Queres eliminar este alumno?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(contextoDialogo, false),
child: const Text('Cancelar'),
),
TextButton(
onPressed: () => Navigator.pop(contextoDialogo, true),
child: const Text('Eliminar'),
),
],
),
);
Neste código:
showDialog()abre o diálogo.builderconstrúe o widget que queremos mostrar.AlertDialogdefine o contido da ventá.titleestablece o título.contentestablece a mensaxe.actionscontén os botóns.
Para pechar o diálogo utilizamos Navigator.pop(), como vimos ao traballar coa navegación entre pantallas.
Neste caso, tamén devolvemos un valor:
Navigator.pop(contextoDialogo, false);
Pecha o diálogo devolvendo false, porque o usuario cancelou a operación.
Navigator.pop(contextoDialogo, true);
Pecha o diálogo devolvendo true, porque o usuario confirmou a eliminación.
💡 Utilizamos
contextoDialogopara identificar o contexto do propio diálogo. Deste xeito, podemos pechalo desde os seus botóns.
⏳ Como esperamos a resposta do usuario?
Cando mostramos un diálogo, necesitamos esperar a que o usuario escolla unha opción antes de continuar coa eliminación.
Para iso utilizamos async e await:
asyncindica que un método pode realizar operacións asíncronas.awaitpermite esperar a que remate unha operación antes de continuar coas seguintes instrucións.
Por exemplo:
final confirmado = await showDialog<bool>(
context: context,
builder: (contextoDialogo) => AlertDialog(
title: const Text('Eliminar alumno?'),
content: const Text('Queres eliminar este alumno?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(contextoDialogo, false),
child: const Text('Cancelar'),
),
TextButton(
onPressed: () => Navigator.pop(contextoDialogo, true),
child: const Text('Eliminar'),
),
],
),
);
A instrución await espera a que se peche o diálogo. Cando isto sucede, gardamos a resposta na variable confirmado.
O resultado pode ser:
| Valor | Significado |
|---|---|
true | O usuario premeu «Eliminar». |
false | O usuario premeu «Cancelar». |
null | O diálogo pechouse sen escoller ningunha opción. |
O tipo <bool> indica que esperamos unha resposta booleana (true ou false), aínda que tamén podemos recibir null se o diálogo se pecha sen responder.
🔍 Como comprobamos a resposta?
Unha vez pechado o diálogo, debemos comprobar se o usuario confirmou a eliminación.
Para iso utilizaremos:
if (!mounted || confirmado != true) return;
Esta condición comproba dúas cousas:
!mounted: comproba se oStatedo widget xa non forma parte da interface.confirmado != true: comproba se o usuario non confirmou a eliminación.
O operador || significa OU, polo que se se cumpre algunha destas condicións, return remata o método e non eliminamos nada.
A propiedade mounted é importante porque, mentres esperamos a resposta do diálogo, podería suceder que a pantalla deixase de existir.
💡 Polo momento, abonda con saber que
mountedevita intentar actualizar unha pantalla que xa non existe. Utilizámolo por seguridade antes de chamar asetState()despois dunha operación asíncrona.
Se a pantalla continúa activa e o usuario confirmou a eliminación, podemos eliminar o alumno mediante removeAt().
Como o método utiliza async e non devolve un resultado útil, declarámolo como Future<void>.
🧪 Exemplo completo: lista de alumnos
Agora imos reunir todo o que aprendemos nunha aplicación que permita engadir e eliminar alumnos.
O programa utiliza:
- Un
TextFieldpara introducir o nome. - Un botón para engadir novos alumnos.
- Un
ListView.builderpara mostrar a lista. - Unha icona para eliminar cada alumno.
- Un
AlertDialogpara confirmar a eliminación.

🔗 DartPad:
🔍 Como se constrúe a lista?
A parte encargada de mostrar os alumnos é:
Expanded(
child: _nomes.isEmpty
? const Center(child: Text('Non hai alumnos'))
: ListView.builder(
itemCount: _nomes.length,
itemBuilder: (context, indice) {
final nome = _nomes[indice];
return ListTile(
title: Text(nome),
trailing: IconButton(
tooltip: 'Eliminar $nome',
icon: const Icon(Icons.delete),
onPressed: () => _eliminar(indice),
),
);
},
),
),
Neste fragmento utilizamos varios elementos que xa coñecemos:
| Instrución | Para que serve? |
|---|---|
Expanded | Permite que a lista ocupe o espazo dispoñible dentro da Column. |
_nomes.isEmpty | Comproba se a lista está baleira. |
itemCount: _nomes.length | Indica cantos alumnos hai na lista. |
itemBuilder | Constrúe as filas da lista segundo se necesitan. |
_nomes[indice] | Obtén o nome do alumno correspondente a esa posición. |
ListTile | Mostra unha fila co nome e a icona de eliminar. |
onPressed: () => _eliminar(indice) | Executa o método para eliminar o alumno seleccionado. |
Tamén utilizamos o operador ternario ? :, que permite escoller entre dúas opcións segundo se cumpra unha condición:
_nomes.isEmpty
? const Center(child: Text('Non hai alumnos'))
: ListView.builder(...)
Se a lista está baleira, mostramos a mensaxe «Non hai alumnos». En caso contrario, mostramos o ListView.builder cos alumnos.
🔄 Que sucede cando modificamos a lista?
Cando engadimos ou eliminamos un alumno, chamamos a setState().
Flutter volve executar build() e actualiza a interface cos datos actuais.
Por exemplo, se inicialmente temos dous alumnos:
['Xurxo', 'Iria']
E engadimos «Roi»:
_nomes.add('Roi');
A lista pasa a conter tres alumnos:
['Xurxo', 'Iria', 'Roi']
Ao reconstruírse a interface, itemCount utiliza a nova lonxitude da lista e ListView.builder pode mostrar os tres alumnos.
💡
ListView.buildernon engade nin elimina elementos por si mesmo. Limítase a construír as filas a partir dos datos que lle proporcionamos. Somos nós quen modificamos a lista mediante métodos comoadd(),removeAt()ouremove().
🧪 Comproba o funcionamento
Executa a aplicación e realiza estas probas:
- Engade un alumno: escribe «Roi» e preme «Engadir». Comproba que aparece na lista.
- Engade outro alumno: escribe «Uxía» e comproba que tamén aparece.
- Intenta engadir un nome baleiro: preme «Engadir» sen escribir nada. Non debería engadirse ningún elemento.
- Cancela unha eliminación: preme a icona de eliminar dun alumno e escolle «Cancelar». O alumno debe continuar na lista.
- Confirma unha eliminación: volve premer a icona e escolle «Eliminar». O alumno debe desaparecer.
- Elimina todos os alumnos: comproba que aparece a mensaxe «Non hai alumnos».
💡 Os datos desta aplicación só se gardan na memoria mentres se executa. Se reiniciamos a aplicación, a lista volverá conter os alumnos definidos inicialmente no código. No Tema 3.6 aprenderemos a gardar información para conservala entre execucións.
O desenvolvemento máis completo de diálogos e seleccións queda para o Tema 3.7.
📚 Consulta: