Flutter调用MET API构建ListView报错排查请求
Hey there! Let's break down the issues in your code and fix that ListView problem step by step. I've identified several key problems that are causing your app to crash and preventing the ListView from rendering:
1. Your getUsers() method isn't collecting multiple objects correctly
Right now, your loop overwrites the body variable with each API request, so you end up with only the last object (when i=4) instead of a list of 5 objects. When you try to call body.map(...), you're trying to use a list method on a single object—this is a major source of your error.
2. No handling for invalid object IDs or failed requests
The MET API's object IDs start at 1, so requesting objects/0 returns an error ({"message":"Object not found"}). Your code doesn't check for successful responses (status code 200) or handle network errors, which will cause parsing failures.
3. Null safety issues in the User class
Many fields returned by the MET API can be null or empty strings, but your User constructor marks all fields as required non-null types. This will cause crashes when parsing objects with missing data.
4. Missing error handling in FutureBuilder
You commented out the error-handling branch in your FutureBuilder. If any request fails, snapshot.hasError will be true, and accessing snapshot.data! directly will throw a null pointer exception.
Fixed Code
Updated getUsers() method
static Future<List<User>> getUsers() async { List<dynamic> objects = []; // Start from 1 (0 is invalid in MET's API) for (int i = 1; i <= 5; i++) { String url = 'https://collectionapi.metmuseum.org/public/collection/v1/objects/$i'; try { var response = await http.get(Uri.parse(url)); // Only process successful responses if (response.statusCode == 200) { var body = json.decode(response.body); // Skip objects without a valid image (to avoid broken CircleAvators) if (body['primaryImageSmall'] != null && body['primaryImageSmall'].isNotEmpty) { objects.add(body); } } } catch (e) { // Log individual request errors without breaking the whole loop print('Failed to fetch object $i: $e'); } } return objects.map<User>(User.fromJson).toList(); }
Updated user.dart (with null safety fixes)
import 'package:flutter/material.dart'; class User { final int objectID; final bool? isHighlight; final String? accessionYear; final String? primaryImage; final String primaryImageSmall; final String? department; final String? objectName; final String? title; final String? culture; final String? period; final String? artistPrefix; final String? artistDisplayName; final String? artistDisplayBio; final String? medium; final String? dimensions; final String? geographyType; final String? city; final String? state; final String? classification; final String? linkResource; final String? galleryNumber; // Renamed to follow Dart naming conventions const User({ required this.objectID, this.isHighlight, this.accessionYear, this.primaryImage, required this.primaryImageSmall, this.department, this.objectName, this.title, this.culture, this.period, this.artistPrefix, this.artistDisplayName, this.artistDisplayBio, this.medium, this.dimensions, this.geographyType, this.city, this.state, this.classification, this.linkResource, this.galleryNumber, }); static User fromJson(json) => User( objectID: json['objectID'] ?? 0, // Fallback to 0 if null isHighlight: json['isHighlight'], accessionYear: json['accessionYear'], primaryImage: json['primaryImage'], primaryImageSmall: json['primaryImageSmall'] ?? '', // Fallback to empty string department: json['department'], objectName: json['objectName'], title: json['title'], culture: json['culture'], period: json['period'], artistPrefix: json['artistPrefix'], artistDisplayName: json['artistDisplayName'], artistDisplayBio: json['artistDisplayBio'], medium: json['medium'], dimensions: json['dimensions'], geographyType: json['geographyType'], city: json['city'], state: json['state'], classification: json['classification'], linkResource: json['linkResource'], galleryNumber: json['GalleryNumber'], // Match API's field name ); }
Updated FutureBuilder in build() method
builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const CircularProgressIndicator(); } else if (snapshot.hasError) { return Center(child: Text('Error: ${snapshot.error}')); } else if (snapshot.data == null || snapshot.data!.isEmpty) { return const Center(child: Text('No objects found')); } else { final users = snapshot.data!; return buildUsers(users); } },
Updated buildUsers() to handle nullable fields
Widget buildUsers(List<User> users) => ListView.builder( itemCount: users.length, itemBuilder: (context, index) { final user = users[index]; return Card( child: ListTile( leading: CircleAvatar( radius: 28, backgroundImage: NetworkImage(user.primaryImageSmall), ), title: Text(user.title ?? 'No Title'), subtitle: Text(user.artistDisplayName ?? 'Unknown Artist'), ), ); }, );
These changes should fix the crashes and get your ListView rendering correctly with MET museum objects. The key fixes are collecting multiple objects properly, handling null values and API errors, and adding robust error handling in the UI.
内容的提问来源于stack exchange,提问作者Nicolò Vianello

