Provider中的watch、read、Consumer、ChangeNotifierProvider、ValueNotifierProvider
一、watch、read、Consumer状态消费API
watch
和 Consumer
都用于监听状态变化并重建 Widget,但 watch
是通过 BuildContext
的扩展方法实现,更简洁,适用于简单场景;Consumer
是一个 Widget,提供了更灵活的构建方式,适用于需要精细控制 Widget 结构的场景。
read
与 watch
和 Consumer
的主要区别在于它不监听状态变化,用于获取状态对象执行非 UI 相关操作,避免不必要的 Widget 重建。在实际使用中,通常会结合 watch
或 Consumer
以及 read
来实现既高效又灵活的状态管理和 UI 更新。
1.1. watch
功能:watch
用于监听状态变化。当被监听的状态对象发生改变时,使用 watch
的 Widget 会自动重建。它内部依赖 InheritedWidget
机制来检测状态变化。
使用场景:适用于需要根据状态变化更新 UI 的场景。例如,在一个计数器应用中,当计数器的值发生变化时,UI 需要显示最新的计数值。
代码示例:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';class Counter with ChangeNotifier {int _count = 0;int get count => _count;void increment() {_count++;notifyListeners();}
}void main() {runApp(ChangeNotifierProvider(create: (context) => Counter(),child: MyApp(),),);
}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text('Provider watch示例'),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [// 使用watch获取并监听Counter状态Text('计数: ${context.watch<Counter>().count}'),ElevatedButton(onPressed: () {context.read<Counter>().increment();},child: Text('增加计数'),),],),),),);}
}
在上述代码中,context.watch<Counter>().count
用于获取 Counter
实例并监听其变化。当调用 increment
方法改变计数器的值时,Text
组件会因为状态变化而重建,从而显示最新的计数值。
1.2. read
功能:read
用于读取状态对象,但不会监听状态变化。它主要用于在不希望 Widget 因状态变化而重建的情况下获取状态对象,例如执行一些与 UI 更新无关的操作,如业务逻辑处理、数据计算等。
使用场景:当你需要获取状态对象来执行一些非 UI 相关的操作时,read
是一个合适的选择。例如,在点击按钮增加计数器值的逻辑中,我们只需要获取 Counter
对象来调用 increment
方法,而不需要监听其变化来更新 UI(因为更新 UI 的操作由 watch
负责)。
代码示例:上述计数器示例中,context.read<Counter>().increment();
就是使用 read
获取 Counter
对象并调用其 increment
方法,这里不需要监听 Counter
的变化,所以使用 read
而不是 watch
。
1.3. Consumer
功能:Consumer
也是用于监听状态变化并重建 Widget。它通过 Builder 回调来构建依赖状态的 Widget。与 watch
不同的是,Consumer
是一个 Widget,它允许你更灵活地控制依赖状态的 Widget 在 Widget 树中的位置和结构。
使用场景:当你希望在 Widget 树的特定位置,基于状态构建一个 Widget 时,Consumer
非常有用。例如,你可能只想在页面的某个特定区域根据状态更新 UI,而不是整个 Widget 因为状态变化而重建。
代码示例:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';class Counter with ChangeNotifier {int _count = 0;int get count => _count;void increment() {_count++;notifyListeners();}
}void main() {runApp(ChangeNotifierProvider(create: (context) => Counter(),child: MyApp(),),);
}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text('Provider Consumer示例'),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [Consumer<Counter>(builder: (context, counter, child) {return Text('计数: ${counter.count}');},),ElevatedButton(onPressed: () {context.read<Counter>().increment();},child: Text('增加计数'),),],),),),);}
}
在上述代码中,Consumer<Counter>
包裹的 Text
组件会根据 Counter
状态的变化而重建,通过 builder
回调构建依赖 Counter
状态的 Widget。
二、ChangeNotifierProvider
用于管理实现了 ChangeNotifier
接口的状态对象。
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';class Counter with ChangeNotifier {int _count = 0;int get count => _count;void increment() {_count++;notifyListeners();}
}void main() {runApp(ChangeNotifierProvider(create: (context) => Counter(),child: MyApp(),),);
}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text('ChangeNotifierProvider示例'),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [Consumer<Counter>(builder: (context, counter, child) {return Text('计数: ${counter.count}');},),ElevatedButton(onPressed: () {context.read<Counter>().increment();},child: Text('增加计数'),),],),),),);}
}
在这个例子中,Counter
类继承自 ChangeNotifier
。ChangeNotifierProvider
提供了 Counter
实例,Consumer
监听 Counter
的变化,当调用 increment
方法并触发 notifyListeners()
时,Consumer
包裹的 Text
会更新。
三、ValueNotifierProvider
用于管理实现了 ValueNotifier
的状态对象。
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';void main() {runApp(ValueNotifierProvider(create: (context) => ValueNotifier<int>(0),child: MyApp(),),);
}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text('ValueNotifierProvider示例'),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [Consumer<ValueNotifier<int>>(builder: (context, valueNotifier, child) {return Text('值: ${valueNotifier.value}');},),ElevatedButton(onPressed: () {context.read<ValueNotifier<int>>().value++;},child: Text('增加值'),),],),),),);}
}
这里 ValueNotifierProvider
提供了一个 ValueNotifier<int>
实例,Consumer
监听其值的变化,通过修改 value
触发更新。
四:示例场景:监听 model 中特定属性并实现局部刷新
假设 model
中有三个属性 a
、b
、c
,只监听其中一个属性,比如 a
。可以通过自定义 ChangeNotifier
类,并在更新 a
时只通知关心 a
的 Consumer
。
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';class MyModel with ChangeNotifier {int a = 0;int b = 0;int c = 0;void updateA(int newA) {a = newA;notifyListeners();}void updateB(int newB) {b = newB;// 不通知,因为我们不关心b的变化对某些Consumer的影响}void updateC(int newC) {c = newC;// 不通知,因为我们不关心c的变化对某些Consumer的影响}
}void main() {runApp(ChangeNotifierProvider(create: (context) => MyModel(),child: MyApp(),),);
}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text('局部刷新示例'),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [Consumer<MyModel>(builder: (context, model, child) {return Text('属性a的值: ${model.a}');},),ElevatedButton(onPressed: () {context.read<MyModel>().updateA(context.read<MyModel>().a + 1);},child: Text('增加属性a的值'),),// 这里省略对b和c的操作示例,因为不关心它们对这个Consumer的影响],),),),);}
}
在上述代码中,MyModel
类继承自 ChangeNotifier
。updateA
方法在更新 a
属性后调用 notifyListeners()
,而 updateB
和 updateC
方法不调用。这样,只有监听 a
属性变化的 Consumer
会在 a
变化时进行局部刷新。如果要更细粒度地控制通知,可以使用 Listenable
或更复杂的状态管理模式来实现只通知特定的 Consumer
。例如,使用 ValueNotifier
分别管理不同的属性,这样每个属性的变化只会通知依赖它的 Consumer
。
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';class MyModel {final ValueNotifier<int> a = ValueNotifier<int>(0);final ValueNotifier<int> b = ValueNotifier<int>(0);final ValueNotifier<int> c = ValueNotifier<int>(0);
}void main() {runApp(ChangeNotifierProvider(create: (context) => MyModel(),child: MyApp(),),);
}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text('局部刷新示例'),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [Consumer<MyModel>(builder: (context, model, child) {return ValueListenableBuilder<int>(valueListenable: model.a,builder: (context, aValue, child) {return Text('属性a的值: $aValue');},);},),ElevatedButton(onPressed: () {context.read<MyModel>().a.value++;},child: Text('增加属性a的值'),),],),),),);}
}
这里使用 ValueNotifier
分别管理 a
、b
、c
属性,ValueListenableBuilder
只监听特定的 ValueNotifier
,从而实现更精确的局部刷新。