Flutter首页多模块API数据加载异常问题咨询
Flutter首页API加载与UI渲染问题
我的Flutter首页包含三个模块:
- 分类列表视图(categories list view)
- 热门美食列表视图(popular food list view)
- 新品美食列表视图(new food list view)
我希望首页加载时一次性获取所有API数据,已经为分类和热门美食模块写了数据获取方法,但现在出现问题:在onInit中调用两个接口方法后,只有一个模块能加载数据;修改代码保存热重载后,另一个模块才会显示数据。请问该如何正确调用API并让UI正常渲染?
现有代码
Home Controller
import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:flutter_admin/consts/consts.dart'; import '../api_service/api_endpoints.dart'; import '../model/category_model.dart'; import '../model/popular_recipe_model.dart'; class HomeController extends GetxController { var currentNavIndex = 0.obs; var isLoading = false.obs; //category list variable List categoryList = [].obs; //popular recipe list variable List popularRecipeList = [].obs; @override void onInit() { fetchCategory(); fetchPopularRecipe(); super.onInit(); } //change navigation tab index changeIndex(index) { currentNavIndex.value = index; } // fetch category Future<List?> fetchCategory() async { var client = http.Client(); isLoading.value = true; try { var response = await client.get(Uri.parse( ApiEndPoints.baseUrl + ApiEndPoints.authEndPoints.fetchCat)); if (response.statusCode == 200) { var jsonString = jsonDecode(response.body); var data = jsonString['data']; categoryList = List.from(data).map((e) => Categories.fromJson(e)).toList(); isLoading.value = false; return categoryList; } else { Get.snackbar("Error", "data not found"); return null; } } finally { client.close(); } } // fetch popular recipe Future<List?> fetchPopularRecipe() async { var client = http.Client(); isLoading.value = true; try { var response = await client.get(Uri.parse( ApiEndPoints.baseUrl + ApiEndPoints.authEndPoints.popularRecipe)); if (response.statusCode == 200) { var jsonString = jsonDecode(response.body); var data = jsonString['data']; popularRecipeList = List.from(data).map((e) => PopularRecipe.fromJson(e)).toList(); isLoading.value = false; return popularRecipeList; } else { Get.snackbar("Error", "data not found"); return null; } } finally { client.close(); } } }
Home Screen(热门美食模块片段)
Obx(()=>SizedBox( height: Dimensions.height300, child: controller.isLoading.value ? const Center(child: CircularProgressIndicator()) : ListView.builder( physics: const BouncingScrollPhysics(), scrollDirection: Axis.horizontal, itemCount: controller.popularRecipeList.length, itemBuilder: (context, index) { return popularRecipe( title: "${controller.popularRecipeList[index].name}", time: "${controller.popularRecipeList[index].recipeTime}", icon: const Icon(Icons.bookmark_outline_sharp), img: "${controller.popularRecipeList[index].imageUrl}"); }), ), ),
问题原因与修复方案
核心问题
- 加载状态冲突:两个API请求共用同一个
isLoading变量,先完成的请求会直接把isLoading设为false,导致未完成请求的模块提前渲染空数据。 - 响应式失效:直接用
=赋值覆盖Observable列表,破坏了GetX的响应式绑定,导致数据更新后UI不刷新。
修复后的代码
Home Controller
import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:flutter_admin/consts/consts.dart'; import '../api_service/api_endpoints.dart'; import '../model/category_model.dart'; import '../model/popular_recipe_model.dart'; class HomeController extends GetxController { var currentNavIndex = 0.obs; // 为每个模块单独设置加载状态 var isCategoryLoading = false.obs; var isPopularLoading = false.obs; // 明确泛型类型,保留Observable列表引用 final categoryList = <Categories>[].obs; final popularRecipeList = <PopularRecipe>[].obs; @override void onInit() { // 同时发起两个请求,提升加载效率 Future.wait([ fetchCategory(), fetchPopularRecipe(), ]); super.onInit(); } void changeIndex(index) { currentNavIndex.value = index; } Future<List<Categories>?> fetchCategory() async { var client = http.Client(); isCategoryLoading.value = true; try { var response = await client.get(Uri.parse( ApiEndPoints.baseUrl + ApiEndPoints.authEndPoints.fetchCat)); if (response.statusCode == 200) { var jsonString = jsonDecode(response.body); var data = jsonString['data']; // 使用assignAll更新列表,保留响应式 categoryList.assignAll( List.from(data).map((e) => Categories.fromJson(e)).toList(), ); isCategoryLoading.value = false; return categoryList; } else { Get.snackbar("Error", "分类数据获取失败"); isCategoryLoading.value = false; return null; } } catch (e) { Get.snackbar("Error", "请求出错: $e"); isCategoryLoading.value = false; return null; } finally { client.close(); } } Future<List<PopularRecipe>?> fetchPopularRecipe() async { var client = http.Client(); isPopularLoading.value = true; try { var response = await client.get(Uri.parse( ApiEndPoints.baseUrl + ApiEndPoints.authEndPoints.popularRecipe)); if (response.statusCode == 200) { var jsonString = jsonDecode(response.body); var data = jsonString['data']; popularRecipeList.assignAll( List.from(data).map((e) => PopularRecipe.fromJson(e)).toList(), ); isPopularLoading.value = false; return popularRecipeList; } else { Get.snackbar("Error", "热门美食数据获取失败"); isPopularLoading.value = false; return null; } } catch (e) { Get.snackbar("Error", "请求出错: $e"); isPopularLoading.value = false; return null; } finally { client.close(); } } }
Home Screen(热门美食模块)
Obx(()=>SizedBox( height: Dimensions.height300, child: controller.isPopularLoading.value ? const Center(child: CircularProgressIndicator()) : ListView.builder( physics: const BouncingScrollPhysics(), scrollDirection: Axis.horizontal, itemCount: controller.popularRecipeList.length, itemBuilder: (context, index) { return popularRecipe( title: "${controller.popularRecipeList[index].name}", time: "${controller.popularRecipeList[index].recipeTime}", icon: const Icon(Icons.bookmark_outline_sharp), img: "${controller.popularRecipeList[index].imageUrl}"); }), ), ),
额外优化点
- 分类模块需同步修改为使用
isCategoryLoading控制加载状态 - 添加异常捕获,避免请求出错时加载状态无法结束
- 明确列表泛型类型,提升代码可读性与类型安全性
内容的提问来源于stack exchange,提问作者MS Droid
相关产品推荐
相关产品推荐

