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

Flutter中修改Provider属性时如何仅触发对应Consumer更新

解决Flutter Provider局部刷新问题

你的问题核心是notifyListeners()会触发所有监听该Provider的Consumer刷新,要实现仅刷新对应状态的Consumer,有两种高效方案:

方案一:拆分独立Provider

将两个计数器拆分为各自独立的ChangeNotifier,这样每个计数器的更新只会通知自己的监听者:

拆分后的Provider代码

class CounterOneProvider with ChangeNotifier {
  int _counter = 1;
  int get counter => _counter;
  
  void increment() {
    _counter++;
    notifyListeners();
  }
}

class CounterTwoProvider with ChangeNotifier {
  int _counter = 2;
  int get counter => _counter;
  
  void increment() {
    _counter++;
    notifyListeners();
  }
}

注册Provider(在入口处)

使用MultiProvider同时注册两个Provider:

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => CounterOneProvider()),
        ChangeNotifierProvider(create: (_) => CounterTwoProvider()),
      ],
      child: const CounterPage(),
    ),
  );
}

页面中的Consumer与按钮

class CounterPage extends StatefulWidget {
  const CounterPage({super.key});
  @override
  State<CounterPage> createState() => _CounterPageState();
}

class _CounterPageState extends State<CounterPage> {
  @override
  Widget build(BuildContext context) => Scaffold(
          body: Column(
        children: [
          Consumer<CounterOneProvider>(builder: (context, value, child) {
            print("Consumer of CounterOne executed");
            return Text("CounterOne: ${value.counter}");
          }),
          Consumer<CounterTwoProvider>(builder: (context, value, child) {
            print("Consumer of CounterTwo executed");
            return Text("CounterTwo: ${value.counter}");
          }),
          ElevatedButton(
            onPressed: () {
              Provider.of<CounterOneProvider>(context, listen: false).increment();
            },
            child: const Text("Increment CounterOne"),
          ),
          ElevatedButton(
            onPressed: () {
              Provider.of<CounterTwoProvider>(context, listen: false).increment();
            },
            child: const Text("Increment CounterTwo"),
          ),
        ],
      ));
}

方案二:使用Consumer的select参数(推荐)

无需拆分Provider,利用Consumer的select参数指定需要监听的状态片段,仅当该片段变化时才触发刷新:

修改原Provider(改用getter属性)

class CounterProvider with ChangeNotifier {
  int _counterOne = 1;
  int get counterOne => _counterOne;
  void incrementCounterOne() {
    _counterOne++;
    notifyListeners();
  }

  int _counterTwo = 2;
  int get counterTwo => _counterTwo;
  void incrementCounterTwo() {
    _counterTwo++;
    notifyListeners();
  }
}

页面中带select参数的Consumer

class CounterPage extends StatefulWidget {
  const CounterPage({super.key});
  @override
  State<CounterPage> createState() => _CounterPageState();
}

class _CounterPageState extends State<CounterPage> {
  late CounterProvider _provider;

  @override
  void initState() {
    _provider = Provider.of<CounterProvider>(context, listen: false);
    super.initState();
  }

  @override
  Widget build(BuildContext context) => Scaffold(
          body: Column(
        children: [
          Consumer<CounterProvider>(
            // 仅监听counterOne的变化
            select: (_, provider) => provider.counterOne,
            builder: (context, value, child) {
              print("Consumer of CounterOne executed");
              return Text("CounterOne: $value");
            },
          ),
          Consumer<CounterProvider>(
            // 仅监听counterTwo的变化
            select: (_, provider) => provider.counterTwo,
            builder: (context, value, child) {
              print("Consumer of CounterTwo executed");
              return Text("CounterTwo: $value");
            },
          ),
          ElevatedButton(
            onPressed: () {
              _provider.incrementCounterOne();
            },
            child: const Text("Increment CounterOne"),
          ),
          ElevatedButton(
            onPressed: () {
              _provider.incrementCounterTwo();
            },
            child: const Text("Increment CounterTwo"),
          ),
        ],
      ));
}

原理说明

select参数返回你关心的状态值,Provider会自动对比前后两次返回的值:

  • 如果值相同,跳过builder刷新
  • 如果值不同,触发对应Consumer的builder重新执行

对于引用类型的状态,需要确保对象是不可变的,或者重写==和hashCode方法,保证Provider能正确判断状态是否变化。

内容的提问来源于stack exchange,提问作者Sayed Naweed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 18:01:06