RiverPod简单使用记录


RiverPod

从Provider到RiverPod

开发时Provider更多的是实现MVVM架构,到Riverpod Viewmodel 的写法会跟之前有一定的区别。

开始

添加依赖

if (use hookWidget)

flutter pub add hooks_riverpod
flutter pub add flutter_hooks

else

flutter pub add flutter_riverpod

.then()=>

flutter pub add riverpod_annotation
flutter pub add dev:riverpod_generator
flutter pub add dev:build_runner
flutter pub add dev:custom_lint
flutter pub add dev:riverpod_lint

初始设置

main.dart

void main() {
  runApp(const ProviderScope(child: MyApp()));
}

ProviderScope
为了Widgets能够读取到Providers,需要使用ProviderScope包裹整个AppWidget。

开发工具代码块插件(Flutter Riverpod Snippets)

VsCode
Android Studio


到这里RiverPod的引入和初始化操作就已经完成。

简单示例

在RivierPod中,页面的Widget推荐使用以下两种:

  • ConsumerWidget || HookConsumerWidget(如果你使用HookWidget) 两种方式都等价于 StatelessWidget
    同样
  • ConsumerStatefulWidget || StatefulHookConsumerWidget == StatefulWidget

以下示例为了代码简洁统一使用 ConsumerWidget

HomePage.dart

/// String
final helloWorldProvider = Provider<String>((ref) => 'Hello Riverpod!');

这行生成一个最简单的Provider,其中:

  • Provider:大多数provider类型的基础,它暴露一个永远不会改变的常量;
  • ref:flutter每一个Widget都有Buildcontext方法,它提供了上下文信息,但ref可在函数中读取到其他的provider,它有三种用法:
    • ref.read:在忽略对象改变的情况下获取provider的值。如:onPress内改变provider值时不需要实时获取ref的值;
    • ref.watch:获取provider到的值并监听更改,当该值改变时,将重新构建订阅该值的widget或provider;
    • ref.listen:在provider上添加监视器,以执行诸如导航到新页面或每当provider更改时显示模态框等操作。

官方作者建议⚠️
尽可能使用 ref.watch 而不是 ref.readref.listen 来实现你的功能。 通过依赖 ref.watch ,你的应用变得既具有响应性又具有声明性,这使得项目会更易于维护。
实际使用
ref.watch 使用于Widget的构建渲染中,因为它是订阅监听的,而如果使用ref.read来渲染组件的话会发现它有一定延迟,大概一秒刷新一次的样子,所以最好将ref.read用于事件的触发函数中。

class HomePage extends ConsumerWidget {
  const HomePage({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.pink[50],
          title: const Text('RiverPod Demo'),
        ),
        body: Center(
          child: Column(
            children: [Text(ref.watch(helloWorldProvider))],// ref.watch
          ),
        ));
  }
}


模拟器运行图片

不同类型的Provider

Provider类型创建Provider的函数使用场景
Provider返回任意类型服务类 / 计算属性 (过滤的列表)
StateProvider返回任意类型过滤条件/简单状态对象
FutureProvider返回任意类型的FutureAPI调用的结果
StreamProvider返回任意类型的StreamAPI返回的Stream
StateNotifierProvider返回StateNotifier的子类一种复杂的状态对象,除了通过接口之外,它是不可变的
ChangeNotifierProvider返回ChangeNotifier的子类需要可变的复杂状态对象

部分Provider使用示例

如果想要更新一个provider的值,可以使用.notifier,该方法会返回一个StateController<T>API文档
.update会返回provider的对象。

  ref.read(countProvider.notifier).update((count) => count += 1);

StateProvider

final countProvider = StateProvider<int>((ref) => 0);
 TextButton(
              onPressed: () {
                ref.read(countProvider.notifier).update((count) => count += 1);
              },
              child: Text(ref.watch(countProvider).toString()),
            ),

FutureProvider

final userNicknameProvider = FutureProvider((ref) async {
// 此处模拟一个耗时任务
  return await Future.delayed(const Duration(seconds: 1), () {
    final nickname = 'Jhin';
    return nickname;
  });
});
   Text('Nickname:${ref.watch(userNicknameProvider).value ?? "加载中..."}'),

在一秒后Text的文本内容会更新为“Jhin”

StateNotifierProvider

通常要改变一个具体的类是需要使用StateNotifierProvider,如用户信息类:UserInfo
可以看到使用StateNotifierProvider不需要像Provider一样自己调用notifyListeners()

class UserInfo {
  UserInfo({
    required this.nickname,
    required this.id,
    required this.count,
  })

  final String nickname;
  final int id;
  int count;

  UserInfo increment() {
    count++;
    return UserInfo(nickname: nickname, id: id, count: count);
  }

  UserInfo decrement() {
    count--;
    return UserInfo(nickname: nickname, id: id, count: count);
  }

  
  String toString() {
    return "{nickname:$nickname},{id:$id},{count:$count}";
  }

  UserInfo userInfo(WidgetRef ref) {
    return UserInfo(nickname: nickname, id: id, count: count);
  }
}

用户类有几个简单属性:用户名,ID,计数器。
如果想展示用户信息并监听计数器的改变就需要创建一个StateNotifierProvider

final userStateNotifyProvider = StateNotifierProvider<UserController, UserInfo>(
    (ref) => UserController(UserInfo(nickname: 'RaBBit', id: 1, count: 20)));// 初始用户信息


class UserController extends StateNotifier<UserInfo> {// 中间层
  UserController(super.state);
  void increment() {
    state = state.increment();
  }

  void decrement() {
    state = state.decrement();
  }
}
TextButton(
           onPressed: () {
                          final UserController controller = ref.watch(userStateNotifyProvider.notifier);
                          controller.increment();
                         },
           child: Text('Count:${ref.watch(userStateNotifyProvider.select((user) => user.count))}'))

其中provider.select方法可以只监听一个provider中具体的值,可以减少数据刷新时的渲染消耗。
模拟器运行图片

组合Provider

provider可以通过ref.watch获取另一个provider

/// 异步联合Provider
final userNicknameProvider = FutureProvider((ref) async {
  return await Future.delayed(const Duration(seconds: 1), () {
    final nickname = ref.watch(userStateNotifyProvider).nickname;
    return nickname;
  });
});

参考

RiverPod官网
Flutter Riverpod 2.0:终极指南
RiverPod 使用记录

作者也还在学习RiverPod的过程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值