什么是 Riverpod?
Riverpod 是由 Flutter 狀態(tài)管理專家 Remi Rousselet 開發(fā)的狀態(tài)管理庫,是 Provider 的繼任者。它解決了 Provider 的許多限制,提供了更好的開發(fā)體驗和更強大的功能:
- 完全避免了
ProviderNotFoundException - 支持自動代碼生成,減少模板代碼
- 更好的類型安全性
- 更簡單的測試
- 輕松組合狀態(tài)
- 自動緩存和重新計算
一、環(huán)境搭建
1. 安裝依賴
在pubspec.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 需要在應(yīng)用的根部包裹ProviderScope:
import 'package:demo_tester/pages/home_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(
// 為整個應(yīng)用提供Riverpod支持
ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
二、基礎(chǔ)概念與第一個 Provider
1. 定義一個簡單的 Provider
// 定義一個簡單的Provider
final helloWorldProvider = Provider((ref) {
return "Hello, Riverpod!";
});
2. 消費 Provider
使用ConsumerWidget(替代StatelessWidget)來消費 Provider:
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) {
// 監(jiān)聽helloWorldProvider并獲取其值
final String message = ref.watch(helloWorldProvider);
return Scaffold(
appBar: AppBar(title: Text("Riverpod Demo")),
body: Center(
child: Text(message),
),
);
}
}
三、常用 Provider 類型
1. StateProvider - 簡單狀態(tài)管理
用于管理簡單的狀態(tài),適合存儲布爾值、數(shù)字、字符串等簡單數(shù)據(jù):
// 定義一個計數(shù)器的StateProvider
final counterProvider = StateProvider((ref) => 0);
使用它:
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) {
// 監(jiān)聽狀態(tài)變化
final count = ref.watch(counterProvider);
return Scaffold(
appBar: AppBar(title: Text("Counter")),
body: Center(child: Text("Count: $count")),
floatingActionButton: FloatingActionButton(
// 更新狀態(tài)
onPressed: () => ref.read(counterProvider.notifier).state++,
child: Icon(Icons.add),
),
);
}
}
2. StateNotifierProvider - 復(fù)雜狀態(tài)管理
當(dāng)需要更復(fù)雜的狀態(tài)邏輯時,使用StateNotifierProvider:
首先創(chuàng)建狀態(tài)類和 StateNotifier:
// 定義狀態(tài)類
class CounterState {
final int count;
CounterState({required this.count});
// 復(fù)制方法,用于創(chuàng)建新狀態(tài)
CounterState copyWith({int? count}) {
return CounterState(count: count ?? this.count);
}
}
// 定義StateNotifier
class CounterNotifier extends StateNotifier<CounterState> {
// 初始化狀態(tài)
CounterNotifier() : super(CounterState(count: 0));
// 增加計數(shù)的方法
void increment() {
state = state.copyWith(count: state.count + 1);
}
// 減少計數(shù)的方法
void decrement() {
state = state.copyWith(count: state.count - 1);
}
// 重置計數(shù)
void reset() {
state = CounterState(count: 0);
}
}
// 創(chuàng)建Provider
final counterNotifierProvider = StateNotifierProvider<CounterNotifier, CounterState>((ref) {
return CounterNotifier();
});
使用它:
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) {
// 監(jiān)聽狀態(tài)
final counterState = ref.watch(counterNotifierProvider);
// 獲取notifier以調(diào)用方法
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 - 處理異步數(shù)據(jù)
用于處理異步操作,如 API 請求:
// 模擬API請求
Future<String> fetchUserData() async {
await Future.delayed(Duration(seconds: 2));
return "John Doe";
}
// 創(chuàng)建FutureProvider
final userProvider = FutureProvider((ref) async {
return fetchUserData();
});
使用它:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class UserPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// 監(jiān)聽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 - 處理流數(shù)據(jù)
用于處理 Stream,如實時數(shù)據(jù)更新:
// 創(chuàng)建一個簡單的計數(shù)器流
Stream<int> countStream() {
return Stream.periodic(Duration(seconds: 1), (count) => count);
}
// 創(chuàng)建StreamProvider
final streamProvider = StreamProvider((ref) {
return countStream();
});
使用它:
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 的一大優(yōu)勢是可以輕松組合多個 Provider:
// 定義兩個簡單的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:
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可以減少模板代碼,提高開發(fā)效率。
1. 創(chuàng)建一個使用代碼生成的 Provider
首先創(chuàng)建一個文件user_provider.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. 運行代碼生成
在終端運行:
flutter packages pub run build_runner build --delete-conflicting-outputs
這會生成user_provider.g.dart文件,包含生成的代碼。
3. 使用生成的 Provider
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),
),
);
}
}