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用法目前没有用到。

相关推荐
shankss1 小时前
Flutter 下拉刷新库 pull_to_refresh_plus 设计与实现分析
flutter
忆江南17 小时前
iOS 深度解析
flutter·ios
明君8799718 小时前
Flutter 实现 AI 聊天页面 —— 记一次 Markdown 数学公式显示的踩坑之旅
前端·flutter
恋猫de小郭19 小时前
移动端开发稳了?AI 目前还无法取代客户端开发,小红书的论文告诉你数据
前端·flutter·ai编程
MakeZero21 小时前
Flutter那些事-交互式组件
flutter
shankss21 小时前
pull_to_refresh_simple
flutter
shankss21 小时前
Flutter 下拉刷新库新特性:智能预加载 (enableSmartPreload) 详解
flutter
SoaringHeart3 天前
Flutter调试组件:打印任意组件尺寸位置信息 NRenderBox
前端·flutter
九狼3 天前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
_squirrel3 天前
记录一次 Flutter 升级遇到的问题
flutter