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
相关产品推荐
相关产品推荐

