You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 01:57:51