Fehlende JSON-Felder
Teil des Abschnitts Daten und Netzwerk der Flutter-Journey von Coddy. Lektion 19 von 27.
Echte Daten weisen Lücken auf. Unter /books haben zwei Bücher einen "subtitle", und bei Pride and Prejudice fehlt er. Für einen nicht vorhandenen Schlüssel ergibt json['subtitle'] den Wert null, und null as String schlägt fehl: Die gesamte Liste kann wegen type 'Null' is not a subtype of type 'String' nicht geladen werden.
Ein Typ, auf den ein ? folgt, kann ebenfalls null enthalten. String? bedeutet „eine Zeichenkette oder nichts“:
final String? subtitle;
subtitle: json['subtitle'] as String?,Dart zwingt dich dann dazu, dich überall dort mit der Lücke auseinanderzusetzen, wo du den Wert verwendest. ?? liefert einen Ersatzwert für null:
Text(book.subtitle ?? 'No subtitle')Du könntest auch book.subtitle! schreiben. Damit sagst du Dart: „Vertrau mir, der Wert ist nicht null.“ Bei Daten von einem Server ist das ein Versprechen, das du nicht halten kannst: Beim Buch ohne Untertitel bringt ! den Bildschirm zum Absturz. Verwende ! nur an Stellen, die du überprüft hast, zum Beispiel bei snapshot.data! nach hasData.
Aufgabe
MittelDas Regal kann nicht geladen werden, weil ein Buch keinen Untertitel hat. Mache den Untertitel optional:
- Das Feld ist ein
String?, lies es alsas String? - Die Liste zeigt
No subtitlefür ein Buch ohne Untertitel an
Probier es selbst
import 'dart:convert';
import 'package:flutter/material.dart';
import 'api.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: BooksScreen(),
);
}
}
class BooksScreen extends StatefulWidget {
const BooksScreen({super.key});
@override
State<BooksScreen> createState() => _BooksScreenState();
}
class _BooksScreenState extends State<BooksScreen> {
late Future<List<Book>> _books;
@override
void initState() {
super.initState();
_books = loadBooks();
}
Future<List<Book>> loadBooks() async {
final body = await fetch('/books');
final items = jsonDecode(body) as List<dynamic>;
return items.map((item) => Book.fromJson(item as Map<String, dynamic>)).toList();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Books')),
body: FutureBuilder<List<Book>>(
future: _books,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(child: Text('Could not load: ${snapshot.error}'));
}
if (snapshot.hasData) {
final books = snapshot.data!;
return ListView.builder(
itemCount: books.length,
itemBuilder: (context, index) {
final book = books[index];
return ListTile(
title: Text(book.title),
subtitle: Text(book.subtitle),
);
},
);
}
return const Center(child: CircularProgressIndicator());
},
),
);
}
}
class Book {
Book({required this.title, required this.subtitle});
final String title;
final String subtitle;
factory Book.fromJson(Map<String, dynamic> json) {
return Book(
title: json['title'] as String,
subtitle: json['subtitle'] as String,
);
}
}
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 NumberFormatÜbe selbstständig: Online-Flutter-Compiler