Riverpod高级用法与技巧:Flutter在鸿蒙平台进阶使用技巧
·


作者:付文龙(红目香薰)
仓库地址:https://gitcode.com/feng8403000/FlutterfromBeginnertoAdvancedForHarmonyOS.git
联系邮箱:372699828@qq.com
引言
Riverpod作为新一代状态管理方案,提供了丰富的高级特性和技巧。掌握这些高级用法可以帮助开发者构建更复杂、更高效的Flutter应用。本文将深入探讨Riverpod的高级用法,包括ref.listen、autoDispose、Family的高级用法、Provider观察者、错误处理等,帮助开发者在鸿蒙平台开发Flutter应用时充分发挥Riverpod的潜力。
ref.listen监听状态变化
基本用法
class CartWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
// 监听购物车变化
ref.listen(cartProvider, (previous, next) {
if (next.length > (previous?.length ?? 0)) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("商品已添加到购物车")),
);
}
});
final cart = ref.watch(cartProvider);
return Text("购物车商品数: ${cart.length}");
}
}
监听部分状态
class UserWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
// 只监听用户名变化
ref.listen(
userProvider.select((user) => user?.name),
(previousName, currentName) {
if (previousName != currentName) {
print("用户名变化: $previousName -> $currentName");
}
},
);
final user = ref.watch(userProvider);
return Text(user?.name ?? "未登录");
}
}
监听异步状态
class DataWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
ref.listen<AsyncValue<List<Post>>>(postsProvider, (previous, next) {
next.whenData((posts) {
print("帖子加载完成: ${posts.length}条");
});
next.whenError((error, stack) {
print("加载失败: $error");
});
});
final posts = ref.watch(postsProvider);
return posts.when(
data: (posts) => ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) => ListTile(title: Text(posts[index].title)),
),
loading: () => const CircularProgressIndicator(),
error: (error, stack) => Text("错误: $error"),
);
}
}
autoDispose自动清理
基本用法
// 自动销毁,节省内存
final userProvider = FutureProvider.autoDispose<User>((ref) async {
final api = ref.read(apiProvider);
return await api.getUser();
});
class UserPage extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(userProvider);
return user.when(
data: (user) => Text(user.name),
loading: () => const CircularProgressIndicator(),
error: (error, stack) => Text("$error"),
);
}
}
keepAlive保持活跃
// 需要保持活跃的Provider
final userProvider = FutureProvider.autoDispose<User>((ref) async {
ref.keepAlive(); // 保持活跃,不自动销毁
final api = ref.read(apiProvider);
return await api.getUser();
});
智能keepAlive
final userProvider = FutureProvider.autoDispose<User>((ref) async {
// 当至少有一个监听者时保持活跃
final link = ref.keepAlive();
// 监听是否有活跃的监听者
ref.listenSelf((previous, next) {
if (next.hasValue) {
link.close(); // 有值后关闭,允许自动销毁
}
});
final api = ref.read(apiProvider);
return await api.getUser();
});
Family高级用法
基本参数化
final userProvider = FutureProvider.family<User, String>((ref, userId) async {
final api = ref.read(apiProvider);
return await api.getUser(userId);
});
class UserProfilePage extends ConsumerWidget {
final String userId;
const UserProfilePage({super.key, required this.userId});
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(userProvider(userId));
return user.when(
data: (user) => Text(user.name),
loading: () => const CircularProgressIndicator(),
error: (error, stack) => Text("$error"),
);
}
}
组合多个参数
final searchProvider = FutureProvider.family<List<Product>, ({String query, int page})>(
(ref, params) async {
final api = ref.read(apiProvider);
return await api.search(params.query, params.page);
},
);
class SearchPage extends ConsumerWidget {
final String query;
const SearchPage({super.key, required this.query});
Widget build(BuildContext context, WidgetRef ref) {
final products = ref.watch(searchProvider((query: query, page: 1)));
return products.when(
data: (products) => ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) => ListTile(title: Text(products[index].name)),
),
loading: () => const CircularProgressIndicator(),
error: (error, stack) => Text("$error"),
);
}
}
autoDispose与Family结合
final userProvider = FutureProvider.autoDispose.family<User, String>((ref, userId) async {
final api = ref.read(apiProvider);
return await api.getUser(userId);
});
// 使用
ref.watch(userProvider("1")); // 当不再使用时自动销毁
Provider观察者
自定义观察者
class ProviderLogger extends ProviderObserver {
void didUpdateProvider(
ProviderBase provider,
Object? previousValue,
Object? newValue,
ProviderContainer container,
) {
print("""
Provider更新:
Provider: ${provider.name ?? provider.runtimeType}
旧值: $previousValue
新值: $newValue
""");
}
void didDisposeProvider(ProviderBase provider, ProviderContainer container) {
print("Provider销毁: ${provider.name ?? provider.runtimeType}");
}
void providerDidFail(
ProviderBase provider,
Object error,
StackTrace stackTrace,
ProviderContainer container,
) {
print("""
Provider错误:
Provider: ${provider.name ?? provider.runtimeType}
错误: $error
堆栈: $stackTrace
""");
}
}
void main() {
runApp(
ProviderScope(
observers: [ProviderLogger()],
child: MyApp(),
),
);
}
性能监控
class PerformanceObserver extends ProviderObserver {
final Map<String, Stopwatch> _timers = {};
void didUpdateProvider(
ProviderBase provider,
Object? previousValue,
Object? newValue,
ProviderContainer container,
) {
final name = provider.name ?? provider.runtimeType.toString();
if (newValue is AsyncLoading) {
_timers[name] = Stopwatch()..start();
} else if (newValue is AsyncData) {
final timer = _timers.remove(name);
if (timer != null) {
timer.stop();
print("Provider $name 加载耗时: ${timer.elapsedMilliseconds}ms");
}
}
}
}
错误处理
全局错误捕获
void main() {
runApp(
ProviderScope(
observers: [ErrorObserver()],
child: MyApp(),
),
);
}
class ErrorObserver extends ProviderObserver {
void providerDidFail(
ProviderBase provider,
Object error,
StackTrace stackTrace,
ProviderContainer container,
) {
// 上报错误到日志服务
print("Provider错误: $error");
// Sentry.captureException(error, stackTrace: stackTrace);
}
}
局部错误处理
final userProvider = FutureProvider<User>((ref) async {
final api = ref.read(apiProvider);
try {
return await api.getUser();
} catch (e) {
// 转换错误类型
throw UserLoadException("加载用户失败", e);
}
});
class UserLoadException implements Exception {
final String message;
final Object? cause;
UserLoadException(this.message, [this.cause]);
String toString() => "UserLoadException: $message";
}
class UserWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(userProvider);
return user.when(
data: (user) => Text(user.name),
loading: () => const CircularProgressIndicator(),
error: (error, stack) {
if (error is UserLoadException) {
return Column(
children: [
Text(error.message),
ElevatedButton(
onPressed: () => ref.invalidate(userProvider),
child: const Text("重试"),
),
],
);
}
return Text("未知错误: $error");
},
);
}
}
核心高级技巧示例
示例1:复杂状态联动
final searchQueryProvider = StateProvider<String>((ref) => "");
final searchResultsProvider = FutureProvider<List<Product>>((ref) async {
final query = ref.watch(searchQueryProvider);
if (query.isEmpty) {
return [];
}
final api = ref.read(apiProvider);
return await api.search(query);
});
final filteredResultsProvider = Provider<List<Product>>((ref) {
final results = ref.watch(searchResultsProvider);
final filter = ref.watch(filterProvider);
return results.when(
data: (products) => products.where(filter.apply).toList(),
loading: () => [],
error: (_, __) => [],
);
});
class SearchFilter {
final double? minPrice;
final double? maxPrice;
final String? category;
SearchFilter({this.minPrice, this.maxPrice, this.category});
bool apply(Product product) {
if (minPrice != null && product.price < minPrice!) return false;
if (maxPrice != null && product.price > maxPrice!) return false;
if (category != null && product.category != category!) return false;
return true;
}
}
final filterProvider = StateProvider<SearchFilter>((ref) => SearchFilter());
class SearchPage extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final query = ref.watch(searchQueryProvider);
final results = ref.watch(filteredResultsProvider);
return Column(
children: [
TextField(
onChanged: (value) => ref.read(searchQueryProvider.notifier).state = value,
decoration: const InputDecoration(labelText: "搜索"),
),
Expanded(
child: ListView.builder(
itemCount: results.length,
itemBuilder: (context, index) {
final product = results[index];
return ListTile(title: Text(product.name));
},
),
),
],
);
}
}
示例2:事件总线模式
final eventBusProvider = Provider<EventBus>((ref) => EventBus());
class EventBus {
final _events = StreamController<AppEvent>.broadcast();
Stream<AppEvent> get events => _events.stream;
void emit(AppEvent event) {
_events.add(event);
}
void dispose() {
_events.close();
}
}
abstract class AppEvent {}
class CartUpdatedEvent extends AppEvent {
final int itemCount;
CartUpdatedEvent(this.itemCount);
}
class NotificationEvent extends AppEvent {
final String message;
NotificationEvent(this.message);
}
// 使用事件总线
class CartNotifier extends StateNotifier<List<CartItem>> {
CartNotifier(this.ref) : super([]);
final Ref ref;
void addItem(CartItem item) {
state = [...state, item];
// 发送事件
ref.read(eventBusProvider).emit(CartUpdatedEvent(state.length));
}
}
// 监听事件
class CartBadgeWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
// 监听事件总线
ref.listen<AsyncValue<AppEvent>>(
eventBusProvider.select((bus) => bus.events),
(previous, next) {
next.whenData((event) {
if (event is CartUpdatedEvent) {
print("购物车更新: ${event.itemCount}");
}
});
},
);
final itemCount = ref.watch(cartItemCountProvider);
return Badge(label: Text("$itemCount"));
}
}
示例3:防抖搜索
final searchQueryProvider = StateProvider<String>((ref) => "");
final debouncedSearchProvider = FutureProvider<List<Product>>((ref) async {
final query = ref.watch(searchQueryProvider);
if (query.isEmpty) {
return [];
}
// 防抖延迟
await Future.delayed(const Duration(milliseconds: 300));
// 如果查询在等待期间改变了,取消请求
final currentQuery = ref.watch(searchQueryProvider);
if (currentQuery != query) {
throw Exception("查询已改变");
}
final api = ref.read(apiProvider);
return await api.search(query);
});
class SearchPage extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final results = ref.watch(debouncedSearchProvider);
return Column(
children: [
TextField(
onChanged: (value) => ref.read(searchQueryProvider.notifier).state = value,
decoration: const InputDecoration(labelText: "搜索"),
),
results.when(
data: (products) => Expanded(
child: ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) => ListTile(title: Text(products[index].name)),
),
),
loading: () => const Expanded(child: Center(child: CircularProgressIndicator())),
error: (error, stack) => const Expanded(child: Center(child: Text(""))),
),
],
);
}
}
示例4:批量状态更新
class BatchUpdateNotifier extends StateNotifier<Map<String, dynamic>> {
BatchUpdateNotifier() : super({});
void updateMultiple({
String? name,
int? count,
bool? enabled,
}) {
state = {
...state,
if (name != null) "name": name,
if (count != null) "count": count,
if (enabled != null) "enabled": enabled,
};
}
void reset() {
state = {};
}
}
final batchProvider = StateNotifierProvider<BatchUpdateNotifier, Map<String, dynamic>>((ref) {
return BatchUpdateNotifier();
});
class BatchUpdateWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(batchProvider);
final notifier = ref.read(batchProvider.notifier);
return Column(
children: [
Text("名称: ${state["name"] ?? "未设置"}"),
Text("数量: ${state["count"] ?? 0}"),
Text("启用: ${state["enabled"] ?? false}"),
ElevatedButton(
onPressed: () => notifier.updateMultiple(
name: "新产品",
count: 10,
enabled: true,
),
child: const Text("批量更新"),
),
],
);
}
}
示例5:条件Provider
final useMockDataProvider = StateProvider<bool>((ref) => false);
final dataProvider = Provider<DataSource>((ref) {
final useMock = ref.watch(useMockDataProvider);
if (useMock) {
return MockDataSource();
}
return RealDataSource();
});
abstract class DataSource {
Future<List<Product>> getProducts();
}
class RealDataSource implements DataSource {
Future<List<Product>> getProducts() async {
await Future.delayed(const Duration(seconds: 1));
return [Product(id: "1", name: "真实商品", price: 100)];
}
}
class MockDataSource implements DataSource {
Future<List<Product>> getProducts() async {
await Future.delayed(const Duration(milliseconds: 100));
return [Product(id: "1", name: "Mock商品", price: 50)];
}
}
final productsProvider = FutureProvider<List<Product>>((ref) async {
final dataSource = ref.watch(dataProvider);
return await dataSource.getProducts();
});
class ProductPage extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final useMock = ref.watch(useMockDataProvider);
final products = ref.watch(productsProvider);
return Column(
children: [
Switch(
value: useMock,
onChanged: (value) => ref.read(useMockDataProvider.notifier).state = value,
),
const Text("使用Mock数据"),
Expanded(
child: products.when(
data: (products) => ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) => ListTile(title: Text(products[index].name)),
),
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stack) => Text("错误: $error"),
),
),
],
);
}
}
示例6:分页加载
final currentPageProvider = StateProvider<int>((ref) => 1);
final productsProvider = FutureProvider<List<Product>>((ref) async {
final page = ref.watch(currentPageProvider);
final api = ref.read(apiProvider);
return await api.getProducts(page);
});
final allProductsProvider = StateNotifierProvider<ProductsNotifier, List<Product>>((ref) {
return ProductsNotifier(ref);
});
class ProductsNotifier extends StateNotifier<List<Product>> {
ProductsNotifier(this.ref) : super([]);
final Ref ref;
bool _isLoading = false;
int _currentPage = 1;
Future<void> loadMore() async {
if (_isLoading) return;
_isLoading = true;
_currentPage++;
ref.read(currentPageProvider.notifier).state = _currentPage;
try {
final newProducts = await ref.read(productsProvider.future);
state = [...state, ...newProducts];
} catch (_) {
_currentPage--;
} finally {
_isLoading = false;
}
}
Future<void> refresh() async {
_currentPage = 1;
ref.read(currentPageProvider.notifier).state = 1;
final newProducts = await ref.read(productsProvider.future);
state = newProducts;
}
}
class ProductListPage extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final products = ref.watch(allProductsProvider);
final notifier = ref.read(allProductsProvider.notifier);
return RefreshIndicator(
onRefresh: () => notifier.refresh(),
child: ListView.builder(
itemCount: products.length + 1,
itemBuilder: (context, index) {
if (index == products.length) {
return const Center(child: CircularProgressIndicator());
}
return ListTile(title: Text(products[index].name));
},
onEndReached: () => notifier.loadMore(),
onEndReachedThreshold: 200,
),
);
}
}
性能优化技巧
使用select减少重建
class UserWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
// 只监听用户名,其他字段变化不会触发重建
final userName = ref.watch(userProvider.select((user) => user?.name ?? ""));
return Text(userName);
}
}
使用watchOnly
class UserWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
// 只监听必要的字段
final (name, email) = ref.watch(
userProvider.select((user) => (user?.name ?? "", user?.email ?? "")),
);
return Column(
children: [Text(name), Text(email)],
);
}
}
避免在build中创建对象
// 错误
class MyWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final style = TextStyle(fontSize: 16); // 每次build都会创建新对象
return Text("Hello", style: style);
}
}
// 正确
class MyWidget extends ConsumerWidget {
static const _style = TextStyle(fontSize: 16); // 复用
Widget build(BuildContext context, WidgetRef ref) {
return Text("Hello", style: _style);
}
}
使用const构造函数
class MyWidget extends ConsumerWidget {
const MyWidget({super.key}); // const构造函数
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(countProvider);
return const Text("Hello"); // const子Widget不会重建
}
}
鸿蒙平台特别考虑
平台特定Provider
final platformProvider = Provider<PlatformService>((ref) {
if (Platform.isAndroid) {
return AndroidPlatformService();
}
return DefaultPlatformService();
});
abstract class PlatformService {
Future<void> showNotification(String message);
}
class AndroidPlatformService implements PlatformService {
Future<void> showNotification(String message) async {
// Android实现
}
}
class DefaultPlatformService implements PlatformService {
Future<void> showNotification(String message) async {
// 默认实现
}
}
权限状态管理
final permissionStatusProvider = StateNotifierProvider<PermissionNotifier, Map<Permission, bool>>((ref) {
return PermissionNotifier(ref);
});
enum Permission { storage, camera, location }
class PermissionNotifier extends StateNotifier<Map<Permission, bool>> {
PermissionNotifier(this.ref) : super({});
final Ref ref;
Future<bool> request(Permission permission) async {
// 鸿蒙平台权限请求
final granted = true; // 实际实现
state = {...state, permission: granted};
return granted;
}
bool has(Permission permission) {
return state[permission] ?? false;
}
}
最佳实践
1. Provider命名规范
// 基础Provider
final appNameProvider = Provider<String>((ref) => "MyApp");
// 状态Provider
final countProvider = StateProvider<int>((ref) => 0);
// StateNotifierProvider
final cartProvider = StateNotifierProvider<CartNotifier, List<CartItem>>((ref) => CartNotifier());
// FutureProvider
final userProvider = FutureProvider<User>((ref) async => await fetchUser());
// StreamProvider
final messagesProvider = StreamProvider<Message>((ref) => messageStream);
// Family
final userByIdProvider = FutureProvider.family<User, String>((ref, id) async => await fetchUser(id));
2. 状态分层
// 服务层
final apiProvider = Provider<ApiService>((ref) => ApiServiceImpl());
// 数据层
final userProvider = FutureProvider<User>((ref) async {
final api = ref.read(apiProvider);
return await api.getUser();
});
// 业务层
final cartProvider = StateNotifierProvider<CartNotifier, List<CartItem>>((ref) => CartNotifier());
// UI层
final cartTotalProvider = Provider<double>((ref) {
final cart = ref.watch(cartProvider);
return cart.fold(0, (sum, item) => sum + item.price);
});
3. 使用扩展方法
extension WidgetRefExtension on WidgetRef {
void showSnackBar(BuildContext context, String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
}
Future<void> refreshData<T>(FutureProvider<T> provider) async {
invalidate(provider);
await read(provider.future);
}
}
// 使用
class MyWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
ref.showSnackBar(context, "Hello");
return Container();
}
}
4. 组合Provider
final authProvider = StateNotifierProvider<AuthNotifier, AuthState>((ref) => AuthNotifier(ref));
final userProvider = FutureProvider<User>((ref) async {
final authState = ref.watch(authProvider);
if (authState.status != AuthStatus.authenticated) {
throw Exception("未登录");
}
final api = ref.read(apiProvider);
return await api.getUser(authState.user!.id);
});
总结
Riverpod的高级用法和技巧为开发者提供了强大的工具来构建复杂的Flutter应用:
- ref.listen:监听状态变化,执行副作用操作。
- autoDispose:自动清理资源,节省内存。
- Family:参数化Provider,支持动态数据获取。
- Provider观察者:监控Provider状态变化,实现日志和错误追踪。
- 错误处理:全局和局部错误捕获,提供良好的用户体验。
- 性能优化:使用select、watchOnly、const构造函数等减少不必要的重建。
在鸿蒙平台开发Flutter应用时,这些高级技巧将帮助开发者构建更高效、更稳定的应用,充分发挥Riverpod的潜力。
更多推荐


所有评论(0)