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

Flutter使用Provider实现筛选功能时UI不更新问题求助

问题分析与修复方案

核心问题1:Provider未触发UI更新通知

你的CustomerProvider中getCustomer方法仅更新了_responseCustomer变量,但没有调用notifyListeners(),导致UI无法感知数据变化。

核心问题2:GridView使用了独立的Provider实例

在页面body部分,你给GridView包裹了一个新的ChangeNotifierProvider,这会创建一个全新的局部CustomerProvider实例,和页面上层获取的Provider不是同一个对象,数据完全隔离,自然不会同步更新。


修复步骤

1. 修正CustomerProvider的getCustomer方法

修改getCustomer,使用已定义的setter(setter中已包含notifyListeners())来自动触发UI更新:

class CustomerProvider with ChangeNotifier {
  ResponseCustomer? _responseCustomer;
  ResponseCustomer? get responseCustomer => _responseCustomer;

  set responseCustomer(ResponseCustomer? responseCustomer) {
   _responseCustomer = responseCustomer;
   notifyListeners();
  }

  Future<ResponseCustomer> getCustomer({String? filter}) async {
     ResponseCustomer responseCustomer =
     await CustomerService().getCustomer(filter: filter);
    // 使用setter自动触发notifyListeners
    this.responseCustomer = responseCustomer;
    debugPrint(responseCustomer.data?.length.toString());
    return responseCustomer;
  }
 }

2. 移除GridView外层的多余Provider

删除GridView所在的ChangeNotifierProvider,直接使用上层Consumer传递的customer实例:

class _CustomerPageState extends State<CustomerPage> {
  getCustomer({String? filter}) async {
    EasyLoading.show(status: 'Loading . . .');
    await Provider.of<CustomerProvider>(context, listen: false)
     .getCustomer(filter: filter);
    EasyLoading.dismiss();
  }
  @override
  Widget build(BuildContext context) {
     return Consumer<CustomerProvider>(
    builder: (context, customer, _) => Scaffold(
          backgroundColor: Colors.white,
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              showModalBottomSheet(
                shape: const RoundedRectangleBorder(
                    borderRadius:
                        BorderRadius.vertical(top: Radius.circular(30))),
                context: context,
                builder: (context) {
                  return StatefulBuilder(builder: (context, setState) {
                    return SizedBox(
                      height: 200,
                      width: MediaQuery.of(context).size.width,
                      child: Padding(
                        padding: const EdgeInsets.all(20.0),
                        child: Column(
                          children: [
                            const SizedBox(height: 20),
                            Row(
                              mainAxisAlignment:
                                  MainAxisAlignment.spaceAround,
                              children: [
                                TextButton(
                                    onPressed: () {
                                      getCustomer(filter: 'all');
                                      Navigator.pop(context);
                                    },
                                    style: TextButton.styleFrom(
                                        backgroundColor: primaryColor),
                                    child: Text('All',
                                        style: whiteTextStyle.copyWith(
                                            fontSize: 13,
                                            fontWeight: medium))),
                                TextButton(
                                    onPressed: () {
                                      getCustomer(filter: 'suspect');
                                      Navigator.pop(context);
                                    },
                                    style: TextButton.styleFrom(
                                        backgroundColor: primaryColor),
                                    child: Text('Suspect',
                                        style: whiteTextStyle.copyWith(
                                            fontSize: 13,
                                            fontWeight: medium))),
                                TextButton(
                                    onPressed: () {
                                      getCustomer(filter: 'low');
                                      Navigator.pop(context);
                                    },
                                    style: TextButton.styleFrom(
                                        backgroundColor: primaryColor),
                                    child: Text('Low',
                                        style: whiteTextStyle.copyWith(
                                            fontSize: 13,
                                            fontWeight: medium))),
                                TextButton(
                                    onPressed: () {
                                      getCustomer(filter: 'medium');
                                      Navigator.pop(context);
                                    },
                                    style: TextButton.styleFrom(
                                        backgroundColor: primaryColor),
                                    child: Text('Medium',
                                        style: whiteTextStyle.copyWith(
                                            fontSize: 13,
                                            fontWeight: medium)))
                              ],
                            ),
                          ],
                        ),
                      ),
                    );
                  });
                },
              );
            },
            backgroundColor: primaryColor,
            child: Icon(
              Icons.filter_list_rounded,
              size: 30,
              color: accentColor,
            ),
          ),
          appBar: AppBar(
            automaticallyImplyLeading: false,
            elevation: 0,
            backgroundColor: primaryColor,
            title: RichText(
              text: TextSpan(
                  text: 'Pelanggan ',
                  style: whiteTextStyle.copyWith(
                      fontSize: 25, fontWeight: bold),
                  children: <TextSpan>[
                    TextSpan(
                        text:
                            '(${customer.responseCustomer?.data?.length})',
                        style: whiteTextStyle.copyWith(
                            fontSize: 15, fontWeight: bold))
                  ]),
            ),
          ),
          body: Padding(
            padding: const EdgeInsets.all(10.0),
            child: Column(
              children: [
                SizedBox(
                    width: MediaQuery.of(context).size.width,
                    height: MediaQuery.of(context).size.height - 166,
                    // 移除此处的ChangeNotifierProvider
                    child: GridView.builder(
                        gridDelegate:
                            const SliverGridDelegateWithMaxCrossAxisExtent(
                                maxCrossAxisExtent: 200,
                                childAspectRatio: 3 / 2,
                                crossAxisSpacing: 3,
                                mainAxisSpacing: 5),
                        itemCount: customer.responseCustomer?.data?.length,
                        itemBuilder: (context, index) {
                          return Card(
                            elevation: 4,
                            color: secondaryColor,
                            child: InkWell(
                              splashColor: accentColor,
                              onTap: () {
                                debugPrint('index number $index tapped');
                              },
                              child: Padding(
                                padding: const EdgeInsets.all(7.0),
                                child: Column(
                                  crossAxisAlignment:
                                      CrossAxisAlignment.start,
                                  children: [
                                    Row(
                                      mainAxisAlignment:
                                          MainAxisAlignment.center,
                                      children: [
                                        Icon(
                                          Icons.account_circle_rounded,
                                          size: 40,
                                          color: accentColor,
                                        )
                                      ],
                                    ),
                                    Text(
                                      '${customer.responseCustomer?.data?[index].nmCustomer}',
                                      style: whiteTextStyle.copyWith(
                                          fontSize: 13, fontWeight: bold),
                                    ),
                                    Text(
                                      '+62 ${customer.responseCustomer?.data?[index].hpCustomer}',
                                      style: whiteTextStyle.copyWith(
                                          fontSize: 13, fontWeight: bold),
                                    ),
                                  ],
                                ),
                              ),
                            ),
                          );
                        },
                      ),
                    ),
              ],
            ),
          ),
        ));
  }
}

额外说明

  • 确保CustomerPage所在的Widget树上层已通过ChangeNotifierProvider注册CustomerProvider(比如MaterialApp外层),否则Provider.of和Consumer会找不到对应实例。
  • 修复后,选择筛选条件调用getCustomer时,Provider会更新数据并通知UI,Consumer会重新构建,AppBar计数和GridView内容都会同步更新。

内容的提问来源于stack exchange,提问作者Mifta Khussolikhin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 23:51:37