Provider 是状态管理的官方推荐的状态管理的库.
flutter官方文档针对状态管理还提供了使用Provider 的一个购物车的例子;
使用下面几个类和方法基本上可以应对大部分场景了.
创建Provider最常用到的类:
- ChangeNotifierProvider
- MultiProvider
- Provider
- ChangeNotifierProxyProvider
访问Provider的state常见方法:
- 使用Provider.of()方法
- 使用Consumer() 方法
例如:
// 使用 Provider.of直接访问model属性
Provider.of<CartModel>(context, listen: true).items;
// 使用 Consumer 访问model
Consumer<CartModel>( builder: (context, cart, child) => Text('\$${cart.totalPrice}', style: hugeStyle));
使用步骤:
1.在需要共享状态的ancestor widget 创建provider 来包裹这个widget,使用create 方法创建 model 或者viewModel实例
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
// Using MultiProvider is convenient when providing multiple objects.
return MultiProvider(
providers: [
// In this sample app, CatalogModel never changes, so a simple Provider
// is sufficient.
Provider(create: (context) => CatalogModel()),
// CartModel is implemented as a ChangeNotifier, which calls for the use
// of ChangeNotifierProvider. Moreover, CartModel depends
// on CatalogModel, so a ProxyProvider is needed.
ChangeNotifierProxyProvider<CatalogModel, CartModel>(
create: (context) => CartModel(),
update: (context, catalog, cart) {
if (cart == null) throw ArgumentError.notNull('cart');
cart.catalog = catalog;
return cart;
},
),
],
child: MaterialApp(
title: 'Provider Demo',
// theme: appTheme,
initialRoute: '/',
routes: {
'/': (context) => const MyLogin(),
'/catalog': (context) => const MyCatalog(),
'/cart': (context) => const MyCart(),
},
),
);
}
}
- 访问provider 所以提供的model 或者viewModel
class _AddButton extends StatelessWidget {
final Item item;
const _AddButton({required this.item});
@override
Widget build(BuildContext context) {
// The context.select() method will let you listen to changes to
// a *part* of a model. You define a function that "selects" (i.e. returns)
// the part you're interested in, and the provider package will not rebuild
// this widget unless that particular part of the model changes.
//
// This can lead to significant performance improvements.
bool isInCart =
Provider.of<CartModel>(context, listen: true).items.contains(item);
// var isInCart = context.select<CartModel, bool>(
// // Here, we are only interested whether [item] is inside the cart.
// (cart) => cart.items.contains(item),
// );
return TextButton(
onPressed: isInCart
? null
: () {
// If the item is not in cart, we let the user add it.
// We are using context.read() here because the callback
// is executed whenever the user taps the button. In other
// words, it is executed outside the build method.
var cart = context.read<CartModel>();
cart.add(item);
},
style: ButtonStyle(
overlayColor: MaterialStateProperty.resolveWith<Color?>((states) {
if (states.contains(MaterialState.pressed)) {
return Theme.of(context).primaryColor;
}
return null; // Defer to the widget's default.
}),
),
child: isInCart
? const Icon(Icons.check, semanticLabel: 'ADDED')
: const Text('ADD'),
);
}
}
class _CartList extends StatelessWidget {
@override
Widget build(BuildContext context) {
var itemNameStyle = Theme.of(context).textTheme.headline6;
// This gets the current state of CartModel and also tells Flutter
// to rebuild this widget when CartModel notifies listeners (in other words,
// when it changes).
var cart = Provider.of<CartModel>(context, listen: true);
// var cart = context.watch<CartModel>();
return ListView.builder(
itemCount: cart.items.length,
itemBuilder: (context, index) => ListTile(
leading: const Icon(Icons.done),
trailing: IconButton(
icon: const Icon(Icons.remove_circle_outline),
onPressed: () {
cart.remove(cart.items[index]);
},
),
title: Text(
cart.items[index].name,
style: itemNameStyle,
),
),
);
}
class _CartTotal extends StatelessWidget {
@override
Widget build(BuildContext context) {
var hugeStyle =
Theme.of(context).textTheme.headline1!.copyWith(fontSize: 48);
return SizedBox(
height: 200,
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Another way to listen to a model's change is to include
// the Consumer widget. This widget will automatically listen
// to CartModel and rerun its builder on every change.
//
// The important thing is that it will not rebuild
// the rest of the widgets in this build method.
Consumer<CartModel>(
builder: (context, cart, child) =>
Text('\$${cart.totalPrice}', style: hugeStyle)),
const SizedBox(width: 24),
TextButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Buying not supported yet.')));
},
style: TextButton.styleFrom(primary: Colors.white),
child: const Text('BUY'),
),
],
),
),
);
}
}
3.如果model 或者viewModel 被修改需要通知状态更新时,model 或者viewModel 需要继承 ChangeNotifier, 调用 notifyListeners(); 方法
class CartModel extends ChangeNotifier {
/// The private field backing [catalog].
late CatalogModel _catalog;
/// Internal, private state of the cart. Stores the ids of each item.
final List<int> _itemIds = [];
/// The current catalog. Used to construct items from numeric ids.
CatalogModel get catalog => _catalog;
set catalog(CatalogModel newCatalog) {
_catalog = newCatalog;
// Notify listeners, in case the new catalog provides information
// different from the previous one. For example, availability of an item
// might have changed.
notifyListeners();
}
/// List of items in the cart.
List<Item> get items => _itemIds.map((id) => _catalog.getById(id)).toList();
/// The current total price of all items.
int get totalPrice =>
items.fold(0, (total, current) => total + current.price);
/// Adds [item] to cart. This is the only way to modify the cart from outside.
void add(Item item) {
_itemIds.add(item.id);
// This line tells [Model] that it should rebuild the widgets that
// depend on it.
notifyListeners();
}
void remove(Item item) {
_itemIds.remove(item.id);
// Don't forget to tell dependent widgets to rebuild _every time_
// you change the model.
notifyListeners();
}
}
网友评论