Limpiar con finally
Parte de la sección Datos y red del Journey de Flutter de Coddy. Lección 12 de 27.
Hay tareas que deben realizarse tanto si una llamada funciona como si falla: ocultar el indicador de carga, volver a habilitar un botón. Es fácil olvidarse de escribirlo en uno de los bloques, try o catch. Dart tiene un tercer bloque para ello, finally:
try {
await saveName(name);
} catch (e) {
// muestra el error
} finally {
// siempre se ejecuta: después de try o después de catch
}finally se ejecuta al final, siempre: después de try cuando funciona, después de catch cuando falla. Eso lo convierte en el lugar adecuado para deshacer lo que preparaste antes de la llamada.
Aquí, la aplicación deshabilita Save mientras guarda, con onPressed: saving ? null : save. No se puede pulsar un botón cuyo onPressed es null. Si saving nunca vuelve a ser false, el botón se queda deshabilitado para siempre.
Desafío
IntermedioGuarda con el campo vacío: aparece el error y Guardar sigue en gris, así que no puedes volver a intentarlo. Mueve saving = false fuera del bloque try y dentro de un bloque finally, en su propio setState.
Pruébalo tú mismo
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: NameScreen(),
);
}
}
class NameScreen extends StatefulWidget {
const NameScreen({super.key});
@override
State<NameScreen> createState() => _NameScreenState();
}
class _NameScreenState extends State<NameScreen> {
final controller = TextEditingController();
String status = '';
bool saving = false;
@override
void dispose() {
controller.dispose();
super.dispose();
}
Future<void> saveName(String name) async {
await Future.delayed(const Duration(seconds: 1));
if (name.isEmpty) {
throw Exception('The name cannot be empty');
}
}
Future<void> save() async {
setState(() {
saving = true;
});
try {
await saveName(controller.text);
setState(() {
status = 'Saved: ${controller.text}';
saving = false;
});
} catch (e) {
setState(() {
status = '$e';
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: controller,
decoration: const InputDecoration(labelText: 'Name'),
),
const SizedBox(height: 16),
ElevatedButton(onPressed: saving ? null : save, child: const Text('Save')),
const SizedBox(height: 16),
Text(status, style: const TextStyle(fontSize: 18)),
],
),
),
);
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Datos y red
3Cuando fallan las llamadas
Capturar errores con tryLanzar un errorLimpiar con finallyEl estado de error de FutureBuilderUn botón para volver a intentarloPractica por tu cuenta: Compilador de Flutter online