Aufräumen mit finally
Teil des Abschnitts Daten und Netzwerk der Flutter-Journey von Coddy. Lektion 12 von 27.
Manche Arbeiten müssen erledigt werden, unabhängig davon, ob ein Aufruf erfolgreich war oder fehlgeschlagen ist: den Ladeindikator ausblenden, eine Schaltfläche wieder aktivieren. Schreibt man den Code sowohl in try als auch in catch, vergisst man ihn leicht an einer der beiden Stellen. Dart hat dafür einen dritten Block: finally:
try {
await saveName(name);
} catch (e) {
// zeige den Fehler an
} finally {
// wird immer ausgeführt: nach try oder nach catch
}finally wird immer zuletzt ausgeführt: nach try, wenn der Aufruf erfolgreich war, und nach catch, wenn er fehlgeschlagen ist. So ist es der eine Ort, an dem du rückgängig machen kannst, was du vor dem Aufruf eingerichtet hast.
Hier blendet die App die Schaltfläche Speichern während des Speicherns aus, mit onPressed: saving ? null : save. Eine Schaltfläche, deren onPressed den Wert null hat, kann nicht gedrückt werden. Wenn saving nicht wieder auf false gesetzt wird, bleibt die Schaltfläche dauerhaft grau.
Aufgabe
MittelSpeichere mit einem leeren Feld: Der Fehler wird angezeigt, und Save bleibt grau, sodass du es nie erneut versuchen kannst. Verschiebe saving = false aus dem try-Block in einen finally-Block, in ein eigenes setState.
Probier es selbst
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)),
],
),
),
);
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Daten und Netzwerk
2Ladebildschirme
Ein Spinner während des LadensFutureBuilder kennenlernenEinmalig in initState ladenDaten per Button neu ladenZum Aktualisieren nach unten ziehen5Datumsangaben und Preise
Datumsangaben mit DateFormatUhrzeiten mit DateFormatPreise mit NumberFormat3Wenn Aufrufe fehlschlagen
Fehler mit try abfangenEinen Fehler auslösenAufräumen mit finallyDer Fehlerzustand von FutureBuilderEine Erneut-versuchen-SchaltflächeÜbe selbstständig: Online-Flutter-Compiler