Flutter中ChangeNotifier获取API数据后UI不更新问题排查
问题:Flutter中Provider获取API数据后UI不更新
在Flutter项目中开发API功能,已成功获取数据,但使用Provider和ChangeNotifier更新UI时,界面无变化。调用API时将listen设为true会导致接口被多次请求,终端无报错。
API Provider代码
class ApiProvider extends ChangeNotifier{ List<PlantModel>? _plantList = []; List<PlantModel>? get plantList => _plantList; final apiService = ApiServices(); bool isLoading = true; void getPlantData()async{ if (kDebugMode) { print("calling...function "); } _plantList = await apiService.getPlantDataCall(); isLoading = false; if (kDebugMode) { print("notifying listners"); } notifyListeners(); if (kDebugMode) { print("listners notified"); print("this is isloading == $isLoading"); } } }
UI代码
import 'package:better_lucks/controller/api_controller.dart'; import 'package:better_lucks/controller/google_sign_in_controller.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:provider/provider.dart'; class Dashboard extends StatelessWidget { Dashboard({super.key}); @override Widget build(BuildContext context) { Provider.of<ApiProvider>(context,listen:false).getPlantData(); final user = FirebaseAuth.instance.currentUser!; return Scaffold( backgroundColor: Colors.green, appBar: AppBar( backgroundColor: Colors.transparent, leading:Container(margin: const EdgeInsets.all(12), child: CircleAvatar(backgroundImage: NetworkImage(user.photoURL!)) ), title: Text(user.displayName.toString(),style:const TextStyle(color:Colors.white),), actions: [ IconButton(color: Colors.white, onPressed: (){ final provider = Provider.of<GoogleSignInProvider>(context,listen: false); provider.logout(); }, icon:const FaIcon(FontAwesomeIcons.arrowRightFromBracket)) ], ), body:Consumer<ApiProvider>( builder: (context,apiProvider,_){ return SizedBox( height: MediaQuery.of(context).size.height, width: MediaQuery.of(context).size.width, child:apiProvider.isLoading? GridView.builder( shrinkWrap: true, gridDelegate:const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, mainAxisSpacing:8, crossAxisSpacing: 8 ), itemCount: apiProvider.plantList!.length, itemBuilder: (BuildContext context,int index){ return Card( child: Column( children: [ Container(margin: const EdgeInsets.all(8), child: CircleAvatar( backgroundImage:NetworkImage(apiProvider.plantList![index].imageUrl), radius: 50, ), ), Text(apiProvider.plantList![index].commonName), ], ), ); }):const Center( child: CircularProgressIndicator(), ), ); },) ); } }
终端输出
I/flutter (16721): we got the response body : I/flutter (16721): [Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel', Instance of 'PlantModel'] I/flutter (16721): notifying listners I/flutter (16721): listners notified I/flutter (16721): this is isloading == false
问题分析与解决方案
1. UI条件判断逻辑颠倒
这是UI不更新的核心原因:当前代码中,isLoading为true时显示GridView,为false时显示加载指示器,但实际逻辑应该是加载中显示指示器,加载完成显示列表。
修正代码:
将Consumer中的条件判断反转:
child: apiProvider.isLoading ? const Center(child: CircularProgressIndicator()) : GridView.builder(...)
2. API重复调用问题
在StatelessWidget的build方法中直接调用getPlantData,会导致每次Widget重建时都触发API请求。解决方法是将Dashboard改为StatefulWidget,在initState中调用API,确保只执行一次:
修改后的Dashboard代码:
class Dashboard extends StatefulWidget { const Dashboard({super.key}); @override State<Dashboard> createState() => _DashboardState(); } class _DashboardState extends State<Dashboard> { @override void initState() { super.initState(); // 仅在初始化时调用一次API Provider.of<ApiProvider>(context, listen: false).getPlantData(); } @override Widget build(BuildContext context) { final user = FirebaseAuth.instance.currentUser!; return Scaffold( backgroundColor: Colors.green, appBar: AppBar( backgroundColor: Colors.transparent, leading: Container( margin: const EdgeInsets.all(12), child: CircleAvatar(backgroundImage: NetworkImage(user.photoURL!)), ), title: Text(user.displayName.toString(), style: const TextStyle(color: Colors.white)), actions: [ IconButton( color: Colors.white, onPressed: () { final provider = Provider.of<GoogleSignInProvider>(context, listen: false); provider.logout(); }, icon: const FaIcon(FontAwesomeIcons.arrowRightFromBracket), ) ], ), body: Consumer<ApiProvider>( builder: (context, apiProvider, _) { return SizedBox( height: MediaQuery.of(context).size.height, width: MediaQuery.of(context).size.width, child: apiProvider.isLoading ? const Center(child: CircularProgressIndicator()) : GridView.builder( shrinkWrap: true, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, mainAxisSpacing: 8, crossAxisSpacing: 8, ), itemCount: apiProvider.plantList!.length, itemBuilder: (BuildContext context, int index) { return Card( child: Column( children: [ Container( margin: const EdgeInsets.all(8), child: CircleAvatar( backgroundImage: NetworkImage(apiProvider.plantList![index].imageUrl), radius: 50, ), ), Text(apiProvider.plantList![index].commonName), ], ), ); }, ), ); }, ), ); } }
3. 额外检查
确保你已在应用顶层正确注册了ApiProvider,例如在main.dart中:
void main() { runApp( ChangeNotifierProvider( create: (context) => ApiProvider(), child: const MyApp(), ), ); }
内容的提问来源于stack exchange,提问作者Abhiram
相关产品推荐
相关产品推荐

