Riverpod使用

听好多人说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用法目前没有用到。

相关推荐
2601_949970696 小时前
HumanizeDuration 跨端三方库仓颉适配全流程
前端·javascript·flutter
传奇开心果编程8 小时前
【Flutter踩坑排雷】第3课:const 误用坑|const 错误使用引发 UI 异常与性能损耗
flutter·移动开发·跨平台·dart·编程语言理论·编程框架·声明式ui
程序员老刘10 小时前
你的Flutter项目要不要紧急升级3.47.8?
flutter·客户端
Talyra4212 小时前
不想开会员,我写了一个记账 App
flutter
2601_9495758616 小时前
JSON5 跨端三方库仓颉适配全流程
前端·javascript·flutter
法的空间16 小时前
AI 编程时代,为什么 Dart 分析会拖慢大型项目的开发
android·flutter·ios
恋猫de小郭18 小时前
Flutter 紧急发布 3.47.8 安全升级版本,建议都升级
android·前端·flutter
好的佩奇1 天前
Dart 之模式
android·flutter·dart
淡淡的香烟2 天前
Flutter实现多语言切换
android·flutter·1024程序员节
事圆则缓2 天前
从零搭建一套 Flutter 架构:先跑通数据流
flutter