状态也可以说是数据,某个组件的数据其他组件可以直接访问,不管中间间隔多少层。
这一功能相当于 Vue 的 Vuex
,React 的 Redux
或者 useContext 钩子函数,Flutter 使用 Provider
实现状态管理。
用法简要步骤:
1. pubspec.yaml
增加 provider
行:
dependencies:
flutter:
sdk: flutter
provider: ^4.0.0 // 最新的版本其实是 6.0.3
保存文件就行,flutter 会自动下载 provider package
2. 设置需要共享的状态/数据
简单地说,就是使用 mixin,例如在类后面加 with ChangeNotifier
, 例如:
// products.dart
import 'package:flutter/material.dart';
import './product.dart';
class Products with ChangeNotifier {
List<Product> _items = [
Product(
id: 'p1',
title: 'Red Shirt',
description: 'A red shirt - it is pretty red!',
price: 29.99,
imageUrl:
'https://cdn.pixabay.com/photo/2017/07/10/17/49/man-2490785_960_720.jpg',
),
];
// getter
List<Product> get items {
return [..._items];
}
Product findById(String id) {
return _items.firstWhere((prod) => prod.id == id);
}
void addProduct() {
// _items.add(item);
notifyListeners();
}
}
3. 在上层组件里加 ChangeNotifierProvider
例如 main.dart
:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import './screens/products_overview_screen.dart';
import './screens/product_detail_screen.dart';
import './providers/products.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return ChangeNotifierProvider(
// 如果 provider 3.0.0, 此处用 builder 属性,
// 但是会出现奇怪的异常,因此使用较高版本,使用 create 属性
create: (_) => Products(),
child: MaterialApp(
title: 'MyShop',
theme: ThemeData(
primarySwatch: Colors.purple,
accentColor: Colors.purple.shade50,
fontFamily: 'Lato',
),
home: ProductsOverviewScreen(),
routes: {
ProductDetailScreen.routeName: (ctx) => ProductDetailScreen(),
}),
);
}
}
4. 底层组件使用 Provider.of<Products>(context)
获得数据
例如:
// products_grid.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import './product_item.dart';
import '../providers/products.dart';
class ProductsGrid extends StatelessWidget {
Widget build(BuildContext context) {
final productsData = Provider.of<Products>(context);
final products = productsData.items; // 获得全部 products
return GridView.builder(
padding: EdgeInsets.all(10),
itemCount: products.length,
itemBuilder: (ctx, i) => ChangeNotifierProvider(
create: (c) => products[i],
child: ProductItem(),
),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 3 / 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
);
}
}
provider
可以嵌套
例如上述代码:
itemBuilder: (ctx, i) => ChangeNotifierProvider(
create: (c) => products[i],
child: ProductItem(),
),
因此product_item.dart
里可以直接获得对应的单个 product
,否则需要使用构造函数传递数据:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../screens/product_detail_screen.dart';
import '../providers/product.dart';
class ProductItem extends StatelessWidget {
Widget build(BuildContext context) {
final product = Provider.of<Product>(context); // 通过 provider 获取 product
return ClipRRect(
borderRadius: BorderRadius.circular(10),
child: GridTile(
child: GestureDetector(
onTap: () {
Navigator.of(context).pushNamed(
ProductDetailScreen.routeName,
arguments: product.id,
);
},
child: Image.network(
product.imageUrl,
fit: BoxFit.cover,
),
),
footer: GridTileBar(
backgroundColor: Colors.black54,
leading: IconButton(
icon: Icon(
product.isFavorite ? Icons.favorite : Icons.favorite_border),
color: Theme.of(context).accentColor,
onPressed: () {
product.toggleFavoriteStatus();
},
),
title: Text(
product.title,
textAlign: TextAlign.center,
),
trailing: IconButton(
icon: Icon(Icons.shopping_cart),
color: Theme.of(context).accentColor,
onPressed: () {},
),
),
),
);
}
}