Flutter API分类数据获取失败:加载指示器持续运行无数据
问题分析与解决方案
你的代码存在几个关键问题,导致加载指示器持续运行且无法展示数据,以下是具体问题和修复步骤:
1. 数据模型与API解析不匹配
你的API返回结构应该是外层包含data数组的对象(比如{"data": [{"id":1,...},...]}),但你在fetchCategory里直接把整个响应当成数组遍历,同时错误地将categories定义为List<Category>,而实际需要的是List<Datum>(每个分类项是Datum实例)。另外Category类里的category_name getter完全无用,还会导致UI调用错误。
2. FutureBuilder重复发起请求
每次Widget build时都会调用fetchCategory(),导致重复发起API请求,同时你依赖state里的categories而不是FutureBuilder的snapshot数据,逻辑混乱。
修复步骤
步骤1:修正数据模型(Category.dart)
删除Category类中无用的category_name getter,模型保持其余部分不变:
import 'dart:convert'; Category categoryFromJson(String str) => Category.fromJson(json.decode(str)); String categoryToJson(Category data) => json.encode(data.toJson()); class Category { List<Datum> data; Category({ required this.data, }); factory Category.fromJson(Map<String, dynamic> json) => Category( data: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))), ); Map<String, dynamic> toJson() => { "data": List<dynamic>.from(data.map((x) => x.toJson())), }; } class Datum { int id; String image; String categoryName; String categoryDes; int activation; DateTime createdAt; DateTime updatedAt; Datum({ required this.id, required this.image, required this.categoryName, required this.categoryDes, required this.activation, required this.createdAt, required this.updatedAt, }); factory Datum.fromJson(Map<String, dynamic> json) => Datum( id: json["id"], image: json["image"], categoryName: json["category_name"], categoryDes: json["category_des"], activation: json["activation"], createdAt: DateTime.parse(json["created_at"]), updatedAt: DateTime.parse(json["updated_at"]), ); Map<String, dynamic> toJson() => { "id": id, "image": image, "category_name": categoryName, "category_des": categoryDes, "activation": activation, "created_at": createdAt.toIso8601String(), "updated_at": updatedAt.toIso8601String(), }; }
步骤2:修正CategorySlider逻辑
修改状态变量类型、API解析逻辑、FutureBuilder使用方式:
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import '../models/Category.dart'; class CategorySlider extends StatefulWidget { const CategorySlider({super.key}); @override State<CategorySlider> createState() => _CategorySliderState(); } class _CategorySliderState extends State<CategorySlider> { List<Datum> categories = []; late Future<List<Datum>> _fetchCategoriesFuture; // 存储Future避免重复请求 @override void initState() { super.initState(); _fetchCategoriesFuture = fetchCategory(); // 在initState里初始化请求 } Future<List<Datum>> fetchCategory() async { final response = await http.get(Uri.parse('http://127.0.0.1:8000/api/category')); if (response.statusCode == 200) { // 正确解析API响应:先转成Category对象,再取data列表 Category categoryResponse = Category.fromJson(jsonDecode(response.body)); setState(() { categories = categoryResponse.data; }); return categoryResponse.data; } else { throw Exception('Failed to load categories'); // 抛出错误便于snapshot捕获 } } @override Widget build(BuildContext context) { return FutureBuilder<List<Datum>>( future: _fetchCategoriesFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { return Center(child: Text('加载失败: ${snapshot.error}')); // 显示错误信息 } else if (snapshot.hasData && snapshot.data!.isNotEmpty) { return ListView.builder( scrollDirection: Axis.horizontal, shrinkWrap: true, padding: const EdgeInsets.only(right: 8), itemCount: snapshot.data!.length, itemBuilder: (BuildContext context, int index) { final category = snapshot.data![index]; return Container( child: Center( child: Column( children: <Widget>[ Padding( padding: const EdgeInsets.all(10.0), child: SizedBox( width: 200, height: 120, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), side: const BorderSide(color: Color(0xFF1818B4)))), onPressed: () {}, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ Text(category.categoryName), // 使用正确的属性名 ], ), ), ), ), ], ), ), ); }, ); } else { return const Center(child: Text('暂无分类数据')); } }); } }
关键修复点说明
- 将
categories类型改为List<Datum>,对应API返回的每个分类项。 - 使用
late Future<List<Datum>>存储请求Future,避免每次build重复发起API请求。 - 正确解析API响应:先将整个响应转为
Category对象,再提取内部的data数组。 - 在FutureBuilder中基于
snapshot.data构建UI,同时处理加载中、错误、空数据等状态。 - 移除UI中错误的
category_name调用,改用Datum类的categoryName属性。
内容的提问来源于stack exchange,提问作者MUHTASIM JEET
相关产品推荐
相关产品推荐

