Champs JSON manquants
Fait partie de la section Données et réseau du Journey Flutter de Coddy. Leçon 19 sur 27.
Les données réelles comportent des lacunes. Dans /books, deux livres ont un "subtitle" et Orgueil et Préjugés n'en a pas. Pour une clé absente, json['subtitle'] renvoie null, et null as String échoue : le chargement de toute la liste échoue avec type 'Null' is not a subtype of type 'String'.
Un type suivi de ? peut aussi contenir null. String?, c'est « une chaîne de caractères, ou rien » :
final String? subtitle;
subtitle: json['subtitle'] as String?,Dart vous oblige alors à gérer cette lacune partout où vous l'utilisez. ?? fournit une valeur de remplacement si la valeur est null :
Text(book.subtitle ?? 'No subtitle')Vous pourriez aussi écrire book.subtitle!, ce qui indique à Dart « faites-moi confiance, cette valeur n'est pas null ». Pour des données provenant d'un serveur, c'est une promesse que vous ne pouvez pas tenir : pour le livre sans sous-titre, ! fait planter l'écran. Réservez ! aux endroits que vous avez vérifiés, comme snapshot.data! après hasData.
Défi
MoyenL’étagère ne se charge pas, car un livre n’a pas de sous-titre. Rends le sous-titre facultatif :
- le champ est un
String?, lis-le avecas String? - la liste affiche
No subtitlepour un livre qui n’en a pas
Essayez vous-même
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,
);
}
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Données et réseau
1Futures
Une valeur qui arrive plus tardAttendre avec async et awaitFonctions qui renvoient des FuturesL’API d’entraînement de Coddy4JSON
Lire du JSONValeurs JSON et asUn constructeur fromJsonUne liste à partir de JSONChamps JSON manquantsEntraînez-vous par vous-même : Compilateur Flutter en ligne