当App变得复杂时,状态在多个Widget间共享变得困难。Provider是Flutter官方推荐的状态管理方案——简单、高效、可测试。
// ✅ 验证通过:Flutter 3.22 + provider: ^6.1.0
// 三种状态管理需求:
// 1. 局部状态 → StatefulWidget + setState
// 2. 跨组件状态 → InheritedWidget / Provider
// 3. 全局状态 → Provider + ChangeNotifier
/// InheritedWidget——Flutter原生的状态传递机制
class CounterInherited extends InheritedWidget {
final int count;
final VoidCallback increment;
const CounterInherited({
required this.count,
required this.increment,
required super.child,
});
static CounterInherited of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<CounterInherited>()!;
}
@override
bool updateShouldNotify(covariant CounterInherited old) => count != old.count;
}
/// 使用InheritedWidget
class CounterProviderApp extends StatefulWidget {
const CounterProviderApp({super.key});
@override
State<CounterProviderApp> createState() => _CounterProviderAppState();
}
class _CounterProviderAppState extends State<CounterProviderApp> {
int _count = 0;
void _increment() => setState(() => _count++);
@override
Widget build(BuildContext context) {
return CounterInherited(
count: _count,
increment: _increment,
child: const _DeepChild(),
);
}
}
class _DeepChild extends StatelessWidget {
const _DeepChild();
@override
Widget build(BuildContext context) {
var inherited = CounterInherited.of(context);
return Column(children: [
Text('计数: ${inherited.count}'),
ElevatedButton(onPressed: inherited.increment, child: const Text('+1')),
]);
}
}
// ✅ 验证通过
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
/// 1. 创建ChangeNotifier模型
class CounterModel extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() { _count++; notifyListeners(); }
void decrement() { _count--; notifyListeners(); }
void reset() { _count = 0; notifyListeners(); }
}
/// 2. 在Widget树顶层提供
void main() {
runApp(ChangeNotifierProvider(
create: (_) => CounterModel(),
child: const MyApp(),
));
}
/// 3. 消费状态——四种方式
class CounterPage extends StatelessWidget {
const CounterPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Provider计数器')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 方式1:context.watch() — 监听变化并重建
Text('watch: ${context.watch<CounterModel>().count}',
style: Theme.of(context).textTheme.headlineMedium),
// 方式2:Consumer — 精确控制重建范围
Consumer<CounterModel>(
builder: (context, model, child) => Text('Consumer: ${model.count}'),
),
// 方式3:context.select() — 监听特定字段
Text('select: ${context.select<CounterModel, int>((m) => m.count)}'),
// 方式4:context.read() — 只读取不监听(回调中用)
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
IconButton(onPressed: () => context.read<CounterModel>().decrement(), icon: const Icon(Icons.remove)),
ElevatedButton(onPressed: () => context.read<CounterModel>().reset(), child: const Text('重置')),
IconButton(onPressed: () => context.read<CounterModel>().increment(), icon: const Icon(Icons.add)),
]),
],
),
),
);
}
}
/// watch vs read vs select 速查:
/// context.watch<M>() → 监听整个模型,任何变化都重建
/// context.read<M>() → 不监听,只用于调用方法
/// context.select<M,T>(f) → 监听特定字段,仅该字段变化时重建
/// Consumer<M> → 精确控制重建的子树范围
// ✅ 验证通过
void main() {
runApp(MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => UserModel()),
ChangeNotifierProvider(create: (_) => CartModel()),
ChangeNotifierProvider(create: (_) => ThemeModel()),
Provider(create: (_) => ApiService()),
// ProxyProvider依赖其他Provider
ProxyProvider<ApiService, UserRepository>(
update: (_, api, __) => UserRepository(apiService: api),
),
ChangeNotifierProxyProvider<UserRepository, UserModel>(
create: (_) => UserModel(),
update: (_, repo, model) => model!..repository = repo,
),
],
child: const MyApp(),
));
}
// ✅ 验证通过:Flutter 3.22 + provider ^6.1.0
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Product {
final String id, name;
final double price;
const Product({required this.id, required this.name, required this.price});
}
class CartItem {
final Product product;
int quantity;
CartItem({required this.product, this.quantity = 1});
double get total => product.price * quantity;
}
class CartModel extends ChangeNotifier {
final List<CartItem> _items = [];
List<CartItem> get items => List.unmodifiable(_items);
int get itemCount => _items.fold(0, (s, i) => s + i.quantity);
double get totalPrice => _items.fold(0.0, (s, i) => s + i.total);
bool get isEmpty => _items.isEmpty;
void add(Product p) {
var existing = _items.where((i) => i.product.id == p.id);
if (existing.isNotEmpty) { existing.first.quantity++; }
else { _items.add(CartItem(product: p)); }
notifyListeners();
}
void remove(String id) { _items.removeWhere((i) => i.product.id == id); notifyListeners(); }
void updateQty(String id, int q) {
if (q <= 0) { remove(id); return; }
var item = _items.where((i) => i.product.id == id).firstOrNull;
if (item != null) { item.quantity = q; notifyListeners(); }
}
void clear() { _items.clear(); notifyListeners(); }
}
void main() => runApp(ChangeNotifierProvider(create: (_) => CartModel(), child: const CartApp()));
class CartApp extends StatelessWidget {
const CartApp({super.key});
@override
Widget build(BuildContext context) => MaterialApp(title: '购物车', home: const ProductListPage());
}
class ProductListPage extends StatelessWidget {
const ProductListPage({super.key});
final products = const [
Product(id: '1', name: '蓝牙耳机', price: 299),
Product(id: '2', name: '机械键盘', price: 499),
Product(id: '3', name: '显示器', price: 1999),
Product(id: '4', name: '鼠标垫', price: 59),
Product(id: '5', name: 'USB-C线', price: 29),
Product(id: '6', name: '充电宝', price: 129),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('商品列表'), actions: [
Consumer<CartModel>(builder: (_, cart, __) => Stack(alignment: Alignment.center, children: [
const Icon(Icons.shopping_cart),
if (cart.itemCount > 0) Positioned(right: 0, top: 0, child: Container(
padding: const EdgeInsets.all(2), decoration: const BoxDecoration(color: Colors.red, shape: BoxShape.circle),
child: Text('${cart.itemCount}', style: const TextStyle(color: Colors.white, fontSize: 10)),
)),
])),
const SizedBox(width: 16),
]),
body: ListView.builder(itemCount: products.length, itemBuilder: (context, i) {
var p = products[i];
return ListTile(
title: Text(p.name), subtitle: Text('¥${p.price}'),
trailing: IconButton(icon: const Icon(Icons.add_shopping_cart), onPressed: () {
context.read<CartModel>().add(p);
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('已添加 ${p.name}'), duration: const Duration(seconds: 1)));
}),
);
}),
);
}
}
class CartPage extends StatelessWidget {
const CartPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('购物车')),
body: Consumer<CartModel>(builder: (_, cart, __) {
if (cart.isEmpty) return const Center(child: Text('购物车是空的'));
return Column(children: [
Expanded(child: ListView(children: cart.items.map((item) => ListTile(
title: Text(item.product.name), subtitle: Text('¥${item.product.price} × ${item.quantity}'),
trailing: Row(mainAxisSize: MainAxisSize.min, children: [
IconButton(onPressed: () => cart.updateQty(item.product.id, item.quantity - 1), icon: const Icon(Icons.remove)),
Text('${item.quantity}'),
IconButton(onPressed: () => cart.updateQty(item.product.id, item.quantity + 1), icon: const Icon(Icons.add)),
IconButton(onPressed: () => cart.remove(item.product.id), icon: const Icon(Icons.delete, color: Colors.red)),
]),
)).toList())),
Container(padding: const EdgeInsets.all(16), decoration: BoxDecoration(color: Theme.of(context).colorScheme.surfaceContainerHigh),
child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Text('合计: ¥${cart.totalPrice.toStringAsFixed(2)}', style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
FilledButton(onPressed: () {}, child: Text('结算 (${cart.itemCount}件)')),
])),
]);
}),
);
}
}
// ✅ 验证通过
/// Selector——精确选择需要的属性,避免不必要重建
class OptimizedWidget extends StatelessWidget {
const OptimizedWidget({super.key});
@override
Widget build(BuildContext context) {
// 只监听count字段,其他变化不触发重建
var count = context.select<CounterModel, int>((m) => m.count);
return Text('Count: $count');
}
}
/// Provider的lazy加载
// 默认Provider是lazy的——首次访问时才创建
// 如果需要立即创建(如后台服务),设置lazy: false
ChangeNotifierProvider(
lazy: false,
create: (_) => BackgroundService(),
child: const MyApp(),
)
/// 多个相同类型的Provider——使用family模式
// 方案1:使用Provider的name区分
// 方案2:创建子类型
abstract class BaseRepository {}
class UserRepository extends BaseRepository {}
class ProductRepository extends BaseRepository {}
// 方案3:使用registerProvider自定义注册
/// Provider与Navigator结合
// 在路由中使用Provider
class DetailPage extends StatelessWidget {
final String itemId;
const DetailPage({required this.itemId, super.key});
@override
Widget build(BuildContext context) {
// 在新路由中仍然可以访问Provider
var cart = context.watch<CartModel>();
return Scaffold(body: Text('Items: ${cart.itemCount}'));
}
}
| 方案 | 学习曲线 | 适用规模 | 特点 |
|---|---|---|---|
| setState | ⭐ | 小型 | 最简单,仅限局部状态 |
| InheritedWidget | ⭐⭐ | 中小型 | Flutter原生,样板代码多 |
| Provider | ⭐⭐ | 中大型 | 官方推荐,简洁高效 |
| Riverpod | ⭐⭐⭐ | 大型 | 编译时安全,更灵活 |
| BLoC | ⭐⭐⭐⭐ | 大型 | 事件驱动,可测试性强 |
| GetX | ⭐ | 中小型 | 最简单,但有争议 |
使用ChangeNotifierProvider实现深色/浅色主题切换。
用Provider重构待办事项App,将状态从StatefulWidget提取到ChangeNotifier。
实现UserModel + CartModel两个Provider,登录后显示用户购物车。
Provider架构师——掌握Flutter官方状态管理方案
下一课:表单与验证——学习Flutter表单系统、验证规则和自定义FormField。