听好多人说riverpod上手比较难度比较高,所以结合使用了几年riverpod的经验写一个入门使用教程。
准备工作
dart
dependencies:
flutter_riverpod: ^3.0.3
dart
void main() { runApp(const ProviderScope(child: MyApp()), ); }
常用的provider
这个provider只是一种叫法,你可以理解为controller也可以
我自己写了很多的项目中常用的provider只有两种 StateProvider, StateNotifierProvider
可能偶尔会用到StreamProvider使用场景比如模拟网络速度的变化
dart
// 每秒中随机一个速度
final mediaSpeedProvider = StreamProvider.autoDispose<int>(
(ref) => Stream.periodic(
const Duration(milliseconds: 1000),
(e) => Random().nextInt(100),
),
);
// widget部分
class Temp extends ConsumerWidget {
const Temp({super.key});
@override
Widget build(BuildContext context, ref) {
final speed = ref.watch(mediaSpeedProvider).value ?? 0;
return Text("current network speed ${speed}kb")
}
}
这里顺便说一下autoDispose它取决于你要不要这个provider的state常驻在app里面, 通俗的讲只要app不死这个provider的值就一直在。
或者换一个说法你要不要这个provider在没有人watch它的时候释放他。
StateProvider使用场景
使用单一数据类型的时候比如bool、int、string、List、Map、或者你的model。
dart
final mediaProvider = StateProvider((ref) => 0.0);
final mediaStatusProvider = StateProvider<int>((ref) => 0);
final copySuccessProvider = StateProvider.autoDispose<bool>((ref) => false);
// 当数据发生变化时
commonRef?.read(mediaStatusProvider.notifier).state = 2;
这里顺便说一下ref,ref相当于一个句柄可以拿到所有provider,ref可以通过ConsumerWidget或者ConsumerStatefulWidget。
如果你不方便拿到ref也可以在全局声明一个比如commRef, 然后在root_page给commRef赋值
其实可以不用深究ref到底是个啥,你只要知道一点没有ref你的provider就都用不。
StateNotifierProvider使用场景
当你的需要多个值去更新ui时,比如一个网络请求
dart
import 'package:hugebox/common/network/network.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'file_list_provider.g.dart';
@Riverpod(keepAlive: true)
class FileListNotifier extends _$FileListNotifier {
int page = 1;
int pageSize = 10;
@override
FileListState build() {
refresh();
return FileListState();
}
void clear() async {
state = state.copyWith(list: []);
}
Future<void> refresh() async {
page = 1;
final res = await NetworkTool.request(
API.open_directory,
params: {
"uid": user?.id,
"page": page, //页面
"size": pageSize, //分页大小
"sorts": [sortStr] //排序方式
},
);
final list = List<FileListModel>.from(res.map((x) => FileListModel.fromJson(x)));
state = state.copyWith(
list: list,
isLoading: false,
noMore: list.length < pageSize,
);
}
Future<bool> loadMore() async {
final res = await NetworkTool.request(
API.open_directory,
params: {
"uid": user?.id,
"page": page, //页面
"size": pageSize, //分页大小
"sorts": [sortStr] //排序方式
},
);
final list = List<FileListModel>.from(res.map((x) => FileListModel.fromJson(x)));
state = state.copyWith(
list: [...state.list, ...list],
isLoading: false,
noMore: list.length < pageSize,
);
return list.length < pageSize;
}
}
class FileListState {
final List<FileListModel> list;
final bool noMore;
final bool isLoading;
FileListState({this.list = const [], this.noMore = false, this.isLoading = true});
FileListState copyWith({
List<FileListModel>? list,
bool? noMore,
bool? isLoading,
}) {
return FileListState(
list: list ?? this.list,
noMore: noMore ?? this.noMore,
isLoading: isLoading ?? this.isLoading,
);
}
}
// widget部分
class Temp extends ConsumerWidget {
const Temp({super.key});
@override
Widget build(BuildContext context, ref) {
final state = ref.watch(fileListNotifierProvider);
ref.listen(fileListNotifierProvider, (old, newValue) {
_controller.finishLoad(newValue.noMore ? IndicatorResult.noMore : IndicatorResult.success);
});
return xxWidget;
}
}
顺便说一下listen的,就是监听provider state的变化,比如请求成功之后需要结束刷新控件的刷新状态。
关于传参
上个StateNotifireProvider使用的是注解的形式可以省去一些工作量,如果不使用注解的话需要自己去实现Notifier比较麻烦推荐使用注解。
传参数直接在build方法里面加就可以了, 这个provider的state还和上面那个FileListNotifier的state是共用的。
dart
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'file_child_list_provider.g.dart';
@Riverpod(keepAlive: true)
class FileChildListNotifier extends _$FileChildListNotifier {
int page = 1;
int pageSize = 20;
@override
FileListState build(String? dirId) {
return FileListState();
}
Future<void> refresh() async {
// xxxx
state = state.copyWith(list: list, isLoading: false);
}
Future<bool> loadMore() async {
// xxx
state = state.copyWith(list: list, isLoading: false);
}
}
// 调用方式
ref.read(fileChildListNotifierProvider(childId).notifier).refresh();
// widget
class Temp extends ConsumerWidget {
const Temp({super.key});
@override
Widget build(BuildContext context, ref) {
final state = ref.watch(fileChildListNotifierProvider(childId));
ref.listen(fileChildListNotifierProvider(childId), (old, newValue) {
_controller.finishLoad(newValue.noMore ? IndicatorResult.noMore : IndicatorResult.success);
});
return xxWidget;
}
}
以上2种基本上可以满足大部分的开发需求了。 关于riverpod3.0+的一些高级的比如重试、override用法目前没有用到。