flutter_riverpod: ^2.6.1 应用笔记

flutter_riverpod: ^2.6.1 应用笔记

flutter_riverpod是一个反应式缓存和数据绑定的框架,Riverpod 提供了下述这些不同类型的 Provider,以满足不同的需求:

  • Provider :只存储 不可变 的值或对象,最简单的状态提供者,只对外提供访问状态值的接口,外部无法对状态值进行修改。
  • FutureProvider :处理 异步操作 ,如:从网络请求数据数据,它会再Future完成时通知其观察者。通常与 autoDispose 修饰符一起使用。
  • StreamProvider :处理 基于流的异步数据,监听一个Stream,并在新数据到达前通知其观察者。

基本使用

  1. 添加依赖
yaml 复制代码
dependencies:
  flutter:
    sdk: flutter
  # Riverpod核心库
  flutter_riverpod: ^2.5.1
  # Riverpod注解
  riverpod_annotation: ^2.3.5
dev_dependencies:
  # Dart代码生成文件
  build_runner:
  # 为Dart和Flutter项目自定义lint规则,Lint有助于捕获潜在错误,并强制执行一致的编程风格
  custom_lint:
  # Riverpod代码生成器
  riverpod_generator: ^2.4.0
  # 专为Riverpod设计的一套lint规则,有助于再使用Riverpod时执行最佳实践
  riverpod_lint: ^2.3.10
  1. 实现计数功能
  • 创建一个状态提供者,StateProvider会观察一个值,并再改变时得到通知

  • 用Riverpod 的 Provider 必须用 ProviderScope 包裹MyApp!!!。

  • 通过ref.watch() 来监听Provider的值,当Provider的值改变时,会自动刷新UI。

  • 获取Provider的通知器修改状态值(自增)。`

    less 复制代码
        import 'package:flutter/material.dart';
        import 'package:flutter_riverpod/flutter_riverpod.dart';
    
        //创建一个状态提供者,StateProvider会观察一个值,并再改变时得到通知
        final clickCountProvider = StateProvider<int>((ref) => 0);
    
        void main() {
         // 想使用Riverpod 的 Provider 必须用 ProviderScope 包裹MyApp!
         runApp(const ProviderScope(child: MyApp()));
        }
    
        //继承ConsumerWidget,它是可以提供监听Provider的Widget
        class MyApp extends ConsumerWidget {
         const MyApp({super.key});
    
         @override
         Widget build(BuildContext context, WidgetRef ref) {
           //   通过ref.watch() 来监听Provider的值,当Provider的值改变时,会自动刷新UI
           final clickCount = ref.watch(clickCountProvider);
           return MaterialApp(
             home: Scaffold(
               appBar: AppBar(title: const Text('Riverpod Demo')),
               body: Center(
                 child: Column(
                   children: [
                     //第一种方式
                     Text('点击了 $clickCount 次'),
                     Builder(
                       builder: (context) => ElevatedButton(
                         onPressed: () {
                           Navigator.push(
                             context,
                             MaterialPageRoute(builder: (context) => const ContPage()),
                           );
                         },
                         child: Text("跳转到计数项目"),
                       ),
                     ),
    
                   ],
                 ),
               ),
             ),
           );
         }
        }

在main.dart文件中创建了一个状态提供者并通过StateProvider观察这个值,当观察的这个值改变时刷新ui。点击按钮跳转到计数页面,在计数页面点击按钮实现数字加一的操作。计数页面代码如下:

dart 复制代码
class ContPage extends ConsumerWidget {
  const ContPage({super.key});
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    //第一种方式
    final int count = ref.watch(clickCountProvider);
    return Scaffold(
      appBar: AppBar(title: const Text('增加计数')),
      body: Center(
        child: Column(
          children: [
            //第一种方式
            Text("点击计数$count"),
            Builder(
              builder: (context) => ElevatedButton(
                onPressed: () {
                  //第一种方式 获取Provider的通知器修改状态值(自增)
                  ref.read(clickCountProvider.notifier).state++;
                },
                child: const Text("点击增加"),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

这样就实现了一个简单的计数功能。

  1. 使用NotifierProvider管理业务状态和业务逻辑实现计数功能

    scala 复制代码
    class ClickCountProviderNotifier extends Notifier<int> {
     //如何修改状态的逻辑
     void increment() {
       // state 表示当前状态
       state++;
     }
    
     // 重写此方法返回Notifier的初始状态
     @override
     int build() => 0;
    }
    
    final clickCountProviderNotifier =
       NotifierProvider<ClickCountProviderNotifier, int>(
         () => ClickCountProviderNotifier(),
       );
    less 复制代码
    class NotifierIncrement extends ConsumerWidget {
     const NotifierIncrement({super.key});
     @override
     Widget build(BuildContext context, WidgetRef ref) {
       // 监听 ClickCountProviderNotifier 的状态
       final count = ref.watch(clickCountProviderNotifierProvider);
       return Scaffold(
         appBar: AppBar(title: const Text('NotifierIncrement')),
         body: Center(
           child: Column(
             children: [
               Text("数字:$count"),
               Builder(
                 builder: (context) => ElevatedButton(
                   onPressed: () {
                     ref
                         .read(clickCountProviderNotifierProvider.notifier)
                         .increment();
                   },
                   child: Text("点击增加计数"),
                 ),
               ),
             ],
           ) );
     }
    }
  2. 使用riverpod实现计数功能

    获取WidgetRef的方式 ,除了上面继承 ConsumerWidget ,直接通过它的 build() 获取外,还可以:

    • 继承 ConsumerStatefulWidget ,通过它的 State.build() 获取。
    • 使用 Consumer/ConsumerBuilder 包裹需要使用 refWidget ,在 builder() 中获取。

    在这里使用了ConsumerBuilder获取WidgetRef。

    scala 复制代码
    @riverpod
    class ClickCountProviderNotifier extends _$ClickCountProviderNotifier {
     @override
     int build() => 0;
    
     void increment() {
       state++;
     }
    }

打开终端 ,执行 flutter pub run build_runner build 生成对应的Provider代码,也可以执行 flutter pub run build_runner watch 监听相关文件改动触发代码文件的重新生成。

  • flutter pub run build_runner build只执行一次。
  • flutter pub run build_runner watch。当代码改变时以监听的方式自动执行。
less 复制代码
//定义的ConsumerBuilder 
ConsumerBuilder consumerBuilder() {
return (BuildContext context, WidgetRef ref, Widget? child) {
// 监听 ClickCountProviderNotifier 的状态
final count = ref.watch(clickCountProviderNotifierProvider);
return Column(
 children: [
 Text("数字:$count"),
 Builder(
 builder: (context) => ElevatedButton(
 onPressed: () {
 ref.read(clickCountProviderNotifierProvider.notifier).increment();
 },
 child: Text("点击增加计数"),
 ),
 ),
 ],
);
};
} 
// ConsumerBuilder
class NotifierIncrement extends StatelessWidget {
const NotifierIncrement({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
 appBar: AppBar(title: const Text('NotifierIncrement')),
 body: Center(child: Consumer(builder: consumerBuilder())),
);
}
}

使用Provider发起你的第一个请求

dart 复制代码
class User {
  final String id;
  final String username;
  final String nickname;
  final DateTime createAt;
  final DateTime updateAt;
  const User({
    required this.id,
    required this.username,
    required this.nickname,
    required this.createAt,
    required this.updateAt,
  });
  factory User.fromJson(Map<String, dynamic> json) => User(
    id: json['id'],
    username: json['username'],
    nickname: json['nickname'],
    createAt: DateTime.fromMillisecondsSinceEpoch(json['createAt'] as int),
    updateAt: DateTime.fromMillisecondsSinceEpoch(json['updateAt'] as int),
  );
}
scala 复制代码
// 基于函数式的方式
// @riverpod
// Future<User> fetchUser(Ref ref) async {
//   final response = await Dio().get(
//     'https://mock.presstime.cn/mock/68807bdbf037b14c6b75f6a2/example/user/1',
//   );
//
//   return User.fromJson(response.data['data']);
// }

//基于类的使用方式
@riverpod
class fetchUser extends _$fetchUser {
  @override
  FutureOr<User> build() async {
    final response = await Dio().get(
      'https://mock.presstime.cn/mock/68807bdbf037b14c6b75f6a2/example/user/1',
    );
    return User.fromJson(response.data['data']);
  }
}

这里的实现方式任选一种即可。

less 复制代码
class MyWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final userAsync = ref.watch(fetchUserProvider);

    return Scaffold(
      appBar: AppBar(title: Text("FutureDemo")),
      body: userAsync.when(
        data: (user) => Text('用户: ${user.nickname}'),
        loading: () => CircularProgressIndicator(),
        error: (error, stack) => Text('错误: $error'),
      ),
    );
  }
}

使用代码生成定义提供者程序时,记住以下几点会很有帮助:

  • 提供者程序可以定义为带注释的函数或 带注释的类。它们几乎相同, 但基于类的提供者程序的优点是包含公共方法,使 外部对象能够修改提供者程序的状态(副作用)。 函数提供者程序是用于编写基于类的提供者程序的语法糖,只有 build 方法, 因此不能由 UI 修改。

  • 支持所有 Dart 异步原语(Future、FutureOr 和 Stream)。

  • 当函数被标记为async时, 提供者程序会自动处理错误/加载状态并公开 AsyncValue。这里引用了官方文档的内容主要为了帮助自己理解:官方文档地址

    函数式的 基于类的
    同步的 @riverpod String example(Ref ref) { return 'foo'; } @riverpod class Example extends _$Example { @override String build() { return 'foo'; } // Add methods to mutate the state }
    异步的 - Future @riverpod Future example(Ref ref) async { return Future.value('foo'); } @riverpod class Example extends _$Example { @override Future build() async { return Future.value('foo'); } // Add methods to mutate the state }
    异步的 - Stream @riverpod Stream example(Ref ref) async* { yield 'foo'; } @riverpod class Example extends _$Example { @override Stream build() async* { yield 'foo'; } // Add methods to mutate the state }

我对Provider 的理解水平仅限于此,虽然也查找了一些资料但都介绍的不够深,这里参考了杰哥的笔记有兴趣的可以去看看。

相关推荐
东风西巷15 分钟前
Rubick:基于Electron的开源桌面效率工具箱
前端·javascript·electron·软件需求
探码科技1 小时前
AI知识管理软件推荐:九大解决方案与企业应用
前端·ruby
编程小黑马1 小时前
解决flutter 在控制器如controller 无法直接访问私有类方法的问题
前端
unfetteredman1 小时前
Error: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.32' not found
前端·javascript·vite
云存储小精灵1 小时前
Dify x 腾讯云 COS MCP:自然语言解锁智能数据处理,零代码构建 AI 新世界
前端·开源
山间板栗1 小时前
微信小程序环境变量设置方案
前端
电商API大数据接口开发Cris2 小时前
Java Spring Boot 集成淘宝 SDK:实现稳定可靠的商品信息查询服务
前端·数据挖掘·api
pepedd8642 小时前
LangChain:大模型开发框架的全方位解析与实践
前端·llm·trae
HANK2 小时前
KLineChart 绘制教程
前端·vue.js
Jerry2 小时前
Compose 利用工具加快开发速度
前端