什么是 Riverpod?
Riverpod 是由 Flutter 状态管理专家 Remi Rousselet 开发的状态管理库,是 Provider 的继任者。它解决了 Provider 的许多限制,提供了更好的开发体验和更强大的功能:
- 完全避免了
ProviderNotFoundException
- 支持自动代码生成,减少模板代码
- 更好的类型安全性
- 更简单的测试
- 轻松组合状态
- 自动缓存和重新计算
一、环境搭建
1. 安装依赖
在pubspec.yaml
中添加以下依赖:
yaml
dependencies:
flutter:
sdk: flutter
flutter_riverpod: ^3.0.0
riverpod_annotation: ^3.0.0
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.7.1
riverpod_generator: ^3.0.0
custom_lint: ^0.8.0
riverpod_lint: ^3.0.0
然后运行flutter pub get
安装依赖。
2. 配置入口
Riverpod 需要在应用的根部包裹ProviderScope
:
dart
import 'package:demo_tester/pages/home_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(
// 为整个应用提供Riverpod支持
ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
二、基础概念与第一个 Provider
1. 定义一个简单的 Provider
dart
// 定义一个简单的Provider
final helloWorldProvider = Provider((ref) {
return "Hello, Riverpod!";
});
2. 消费 Provider
使用ConsumerWidget
(替代StatelessWidget
)来消费 Provider:
dart
import 'package:demo_tester/pages/combined_provider_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class HomePage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// 监听helloWorldProvider并获取其值
final String message = ref.watch(helloWorldProvider);
return Scaffold(
appBar: AppBar(title: Text("Riverpod Demo")),
body: Center(
child: Text(message),
),
);
}
}
三、常用 Provider 类型
1. StateProvider - 简单状态管理
用于管理简单的状态,适合存储布尔值、数字、字符串等简单数据:
dart
// 定义一个计数器的StateProvider
final counterProvider = StateProvider((ref) => 0);
使用它:
dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_riverpod/legacy.dart';
class CounterPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// 监听状态变化
final count = ref.watch(counterProvider);
return Scaffold(
appBar: AppBar(title: Text("Counter")),
body: Center(child: Text("Count: $count")),
floatingActionButton: FloatingActionButton(
// 更新状态
onPressed: () => ref.read(counterProvider.notifier).state++,
child: Icon(Icons.add),
),
);
}
}
2. StateNotifierProvider - 复杂状态管理
当需要更复杂的状态逻辑时,使用StateNotifierProvider
:
首先创建状态类和 StateNotifier:
dart
// 定义状态类
class CounterState {
final int count;
CounterState({required this.count});
// 复制方法,用于创建新状态
CounterState copyWith({int? count}) {
return CounterState(count: count ?? this.count);
}
}
// 定义StateNotifier
class CounterNotifier extends StateNotifier<CounterState> {
// 初始化状态
CounterNotifier() : super(CounterState(count: 0));
// 增加计数的方法
void increment() {
state = state.copyWith(count: state.count + 1);
}
// 减少计数的方法
void decrement() {
state = state.copyWith(count: state.count - 1);
}
// 重置计数
void reset() {
state = CounterState(count: 0);
}
}
// 创建Provider
final counterNotifierProvider = StateNotifierProvider<CounterNotifier, CounterState>((ref) {
return CounterNotifier();
});
使用它:
dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_riverpod/legacy.dart';
class AdvancedCounterPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// 监听状态
final counterState = ref.watch(counterNotifierProvider);
// 获取notifier以调用方法
final counterNotifier = ref.read(counterNotifierProvider.notifier);
return Scaffold(
appBar: AppBar(title: Text("Advanced Counter")),
body: Center(child: Text("Count: ${counterState.count}")),
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: () => counterNotifier.decrement(),
child: Icon(Icons.remove),
),
SizedBox(width: 10),
FloatingActionButton(
onPressed: () => counterNotifier.reset(),
child: Icon(Icons.refresh),
),
SizedBox(width: 10),
FloatingActionButton(
onPressed: () => counterNotifier.increment(),
child: Icon(Icons.add),
),
],
),
);
}
}
3. FutureProvider - 处理异步数据
用于处理异步操作,如 API 请求:
dart
// 模拟API请求
Future<String> fetchUserData() async {
await Future.delayed(Duration(seconds: 2));
return "John Doe";
}
// 创建FutureProvider
final userProvider = FutureProvider((ref) async {
return fetchUserData();
});
使用它:
dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class UserPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// 监听FutureProvider
final userAsyncValue = ref.watch(userProvider);
return Scaffold(
appBar: AppBar(title: Text("User Data")),
body: Center(
child: userAsyncValue.when(
loading: () => CircularProgressIndicator(),
error: (error, stack) => Text("Error: $error"),
data: (user) => Text("User: $user"),
),
),
);
}
}
4. StreamProvider - 处理流数据
用于处理 Stream,如实时数据更新:
dart
// 创建一个简单的计数器流
Stream<int> countStream() {
return Stream.periodic(Duration(seconds: 1), (count) => count);
}
// 创建StreamProvider
final streamProvider = StreamProvider((ref) {
return countStream();
});
使用它:
dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class StreamPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final streamAsyncValue = ref.watch(streamProvider);
return Scaffold(
appBar: AppBar(title: Text("Stream Demo")),
body: Center(
child: streamAsyncValue.when(
loading: () => CircularProgressIndicator(),
error: (error, stack) => Text("Error: $error"),
data: (count) => Text("Seconds passed: $count"),
),
),
);
}
}
四、Provider 组合
Riverpod 的一大优势是可以轻松组合多个 Provider:
dart
// 定义两个简单的Provider
final nameProvider = Provider((ref) => "John");
final ageProvider = Provider((ref) => 30);
// 组合上面两个Provider
final userInfoProvider = Provider((ref) {
final name = ref.watch(nameProvider);
final age = ref.watch(ageProvider);
return "Name: $name, Age: $age";
});
使用组合后的 Provider:
dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class CombinedProviderPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final userInfo = ref.watch(userInfoProvider);
return Scaffold(
appBar: AppBar(title: Text("Combined Providers")),
body: Center(child: Text(userInfo)),
);
}
}
五、自动代码生成
使用riverpod_generator
可以减少模板代码,提高开发效率。
1. 创建一个使用代码生成的 Provider
首先创建一个文件user_provider.dart
:
dart
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'user_provider.g.dart';
@riverpod
String userName(Ref ref) {
return "Alice";
}
@riverpod
int userAge(Ref ref) {
return 25;
}
@riverpod
String userDetails(Ref ref) {
final name = ref.watch(userNameProvider);
final age = ref.watch(userAgeProvider);
return "$name is $age years old";
}
2. 运行代码生成
在终端运行:
bash
flutter packages pub run build_runner build --delete-conflicting-outputs
这会生成user_provider.g.dart
文件,包含生成的代码。
3. 使用生成的 Provider
dart
import 'package:demo_tester/provider/user_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class GeneratedProviderPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final details = ref.watch(userDetailsProvider);
return Scaffold(
appBar: AppBar(title: Text("Generated Providers")),
body: Center(
child: Text(details),
),
);
}
}