Flutter从服务端请求文档构建列表项时加载失败问题
Flutter请求返回200但抛出‘Failed to load album’异常排查
我有一个名为ProductDatabase的集合,存储着所有可用产品的文档。现在要通过带身份验证的请求访问本地服务器,获取所有文档快照,用来在Flutter中构建ListTile。
当前本地服务器运行正常,执行代码时命令提示符显示请求已发送且返回状态码200,但仍抛出‘Failed to load album’异常。相关代码如下:
import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:redue/pages/menu.dart'; import '../main.dart'; import 'listProducts.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:favorite_button/favorite_button.dart'; import 'map.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; class listPage1 extends StatefulWidget { const listPage1({super.key}); @override State<listPage1> createState() => listPage3(); } class listPage3 extends State<listPage1> { late Future<Album> futureAlbum; Widget _buildListItem(BuildContext context, DocumentSnapshot document) { return Container( child: FutureBuilder<List<Album>>( future: fetchAlbum(), builder: (context, snapshot) { if (snapshot.hasData) { return ListView.builder( itemCount: snapshot.data!.length, itemBuilder: (context, index) { return ListTile( title: Text(snapshot.data![index].productName), ); }); } else { return const CircularProgressIndicator(); } })); } } Future<List<Album>> fetchAlbum() async { String username = 'xxxxxx'; String password = 'xxxxxxxx'; String basicAuth = 'Basic ' + base64Encode(utf8.encode('$username:$password')); print(basicAuth); final r = await http.get( Uri.parse( 'http://0.0.0.0:5000/apiv1/read?collection=ProductDatabase&document=all'), headers: <String, String>{'authorization': basicAuth}); print(r.body); if (r.statusCode == 200) { // If the server did return a 200 OK response, // then parse the JSON. return json.decode(r.body).cast<Album>(); } else { // If the server did not return a 200 OK response, // then throw an exception. throw Exception('Failed to load album'); } } } class Album { final String brand; final int quantity; final String SBD; final String storeName; final String price; final String productName; final String ID; const Album({ required this.brand, required this.quantity, required this.SBD, required this.storeName, required this.price, required this.productName, required this.ID, }); factory Album.fromJson(Map<String, dynamic> json) { return Album( brand: json["Data"]['Brand'], quantity: json["Data"]['quantity'], SBD: json["Data"]['SBD'], storeName: json["Data"]['storeName'], price: json["Data"]['price'], productName: json["Data"]['productName'], ID: json['ID'], ); } }
问题根源
- JSON解析逻辑错误:
json.decode(r.body).cast<Album>()无法直接将JSON数组转换为Album对象列表,cast方法不会自动调用Album.fromJson构造器,这是抛出异常的核心原因。 - FutureBuilder使用不当:每次调用
_buildListItem都会重新执行fetchAlbum(),导致重复请求和状态混乱;同时未处理snapshot.hasError的情况,无法直观看到具体错误。 - 数据结构可能不匹配:需确认服务器返回的JSON结构是否与
Album.fromJson中的字段对应,比如是否存在外层Data字段、字段名大小写是否一致。
修复方案
1. 修正JSON解析逻辑
将数组遍历并逐个转换为Album对象:
Future<List<Album>> fetchAlbum() async { String username = 'xxxxxx'; String password = 'xxxxxxxx'; String basicAuth = 'Basic ' + base64Encode(utf8.encode('$username:$password')); print(basicAuth); final r = await http.get( Uri.parse( 'http://0.0.0.0:5000/apiv1/read?collection=ProductDatabase&document=all'), headers: <String, String>{'authorization': basicAuth}); print(r.body); if (r.statusCode == 200) { List<dynamic> jsonList = json.decode(r.body); return jsonList.map((item) => Album.fromJson(item)).toList(); } else { throw Exception('Failed to load album'); } }
2. 优化FutureBuilder使用
在State初始化时仅调用一次请求,避免重复发起:
class listPage3 extends State<listPage1> { late Future<List<Album>> futureAlbums; @override void initState() { super.initState(); futureAlbums = fetchAlbum(); } @override Widget build(BuildContext context) { return Scaffold( body: FutureBuilder<List<Album>>( future: futureAlbums, builder: (context, snapshot) { if (snapshot.hasData) { return ListView.builder( itemCount: snapshot.data!.length, itemBuilder: (context, index) { return ListTile( title: Text(snapshot.data![index].productName), ); }, ); } else if (snapshot.hasError) { return Text('加载错误: ${snapshot.error}'); } return const CircularProgressIndicator(); }, ), ); } // 修正后的fetchAlbum方法放在这里 }
3. 验证并调整数据结构匹配
根据服务器返回的JSON结构修改Album.fromJson,比如如果返回的是直接的产品对象数组(无外层Data),则调整为:
factory Album.fromJson(Map<String, dynamic> json) { return Album( brand: json['Brand'], quantity: json['quantity'], SBD: json['SBD'], storeName: json['storeName'], price: json['price'], productName: json['productName'], ID: json['ID'], ); }
内容的提问来源于stack exchange,提问作者ReDue
相关产品推荐
相关产品推荐

