Các trường JSON bị thiếu
Thuộc phần Dữ liệu và mạng trong hành trình Flutter của Coddy. Bài học 19 trên 27.
Dữ liệu thực tế thường có chỗ thiếu. Trong /books, hai cuốn sách có "subtitle", còn Pride and Prejudice thì không. Với khóa không có trong dữ liệu, json['subtitle'] trả về null, còn null as String gây lỗi: toàn bộ danh sách không tải được với lỗi type 'Null' is not a subtype of type 'String'.
Kiểu dữ liệu có thêm ? ở phía sau cũng có thể chứa null. String? có nghĩa là «một chuỗi hoặc không có gì»:
final String? subtitle;
subtitle: json['subtitle'] as String?,Dart buộc bạn xử lý chỗ thiếu dữ liệu ở bất cứ nơi nào bạn dùng giá trị đó. ?? cung cấp giá trị dự phòng khi gặp null:
Text(book.subtitle ?? 'No subtitle')Bạn cũng có thể viết book.subtitle!, để báo với Dart rằng «cứ tin mình, giá trị này không phải null». Với dữ liệu từ máy chủ, bạn không thể đảm bảo điều đó: khi gặp cuốn sách không có phụ đề, ! sẽ làm màn hình gặp sự cố. Chỉ dùng ! ở những chỗ bạn đã kiểm tra, chẳng hạn snapshot.data! sau khi kiểm tra hasData.
Thử thách
Trung bìnhKệ sách không tải được vì một quyển sách không có phụ đề. Hãy đặt phụ đề thành tùy chọn:
- trường này có kiểu
String?, đọc bằngas String? - danh sách hiển thị
No subtitlecho quyển sách không có phụ đề
Tự làm thử
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,
);
}
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Dữ liệu và mạng
Tự luyện tập: Trình biên dịch Flutter online