Menu
CoddyTech

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.

challenge icon

Aufgabe

Mittel

Das Regal kann nicht geladen werden, weil ein Buch keinen Untertitel hat. Mache den Untertitel optional:

  • Das Feld ist ein String?, lies es als as String?
  • Die Liste zeigt No subtitle fü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,
    );
  }
}
quiz iconTeste dich selbst

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

Übe selbstständig: Online-Flutter-Compiler