[译][官方文档] Flutter/Dart 状态管理库 Riverpod - 概念 - 关于代码生成

原文链接:About code generation | Riverpod

pub:riverpod | Dart Package (flutter-io.cn)

译时版本: 2.4.9


之前翻译过 Riverpod 的官方文档,现在随着版本更新,官方文档又多了很多新内容,所以再补充翻译一下。

之前翻译过的内容,现在官方文档有中文了。
Flutter状态管理库Riverpod官方文档翻译汇总 - 掘金 (juejin.cn)


关于代码生成

代码生成的想法是使用工具为我们生成代码。在 Dart 中,带来的副作用就是需要"编译"应用的附加步骤。该问题在不久的将来可能会被解决,因为 Dart 开发组已经在致力于该问题的潜在解决方案。

在 Riverpod 的上下文中,代码生成多少会改变定义一个 "provider" 的语法。例如,代替以下代码:

dart 复制代码
final fetchUserProvider = FutureProvider.autoDispose.family<User, int>((ref, userId) async {
  final json = await http.get('api/user/$userId');
  return User.fromJson(json);
});

使用代码生成,会如下编写:

dart 复制代码
@riverpod
Future<User> fetchUser(FetchUserRef ref, {required int userId}) async {
  final json = await http.get('api/user/$userId');
  return User.fromJson(json);
}

使用 Riverpod 时,代码生成完全是可选项。使用 Riverpod 时不使用代码生成也是完全可以的。同时,Riverpod 也拥抱代码生成并建议使用它。

如何安装和使用 Riverpod 代码生成器的信息,参考开始页面。确认下文档的侧边栏代码生成是可用的。

应该使用代码生成吗?

在 Riverpod 中代码生成是可选的。有了这个意识,可能想知道应该不应该使用代码生成。

回答是:大概是应该使用

使用代码生成是使用 Riverpod 的建议方式。这是更有前瞻性的途径并且这会尽力挖掘出 Riverpod 的最大潜力。

同时,很多应用已经使用了 Freezed 或 json_serializable 之类代码生成的包了。

这些情况下,项目很可能已经配置好了代码生成,使用 Riverpod 也会比较简单。

现在,代码生成是可选项是因为 build_runner 很多情况下不被推荐。但是既然 Dart 中静态元编程 是可用的,build_runner 将不会再是问题。因为这一点,在 Riverpod 中代码生成语法就将会是唯一可用的语法。

如果使用 build_runner 会带来破坏性的问题,那需要考虑的就是不要使用代码生成。但是这样也要意识到会错失一些特性,并且将来也必然会迁移到代码生成。

尽管如此,到时候 Riverpod 肯也会提供迁移工具使转换尽可能平滑。

使用代码生成的好处是什么?

你可能想知道:"如果在 Riverpod 中代码生成是可选的,那为什么使用它呢?"

通常使用包:会让生活更容易。这包括但不限于:

  • 更好的语法,更具可读性/灵活性,并且有更简单的学习曲线。

    • 不需要担心 provider 的类型。编写业务逻辑,然后选择最适合的 provider 。
    • 语法不再像是定义"难受的全局变量"。取而代之的是定义自定义的函数/类。
    • 向 provider 传递参数现在是不受限的。相比于使用 .family 的受限和传递单个占位参数,现在可以传递任意参数。包括命名参数,可选参数甚至是默认值。
  • 在 Rivderpod 中所编写代码的状态热重载

  • 调试器可拾取通过生成的附加元数据进行更好的调试。

  • Riverpod 的一些特性只在代码生成中可用。

语法

定义一个 provider :

使用代码生成定义一个 provider 时,意识到以下几点会有帮助:

  • Provider 可定义为一个注解的函数或注解的类它们几乎是相同的,但是基于类的 provider 有几个优点,它能包含公共方法可扩展外部对象以修改 provider 的状态(副作用)。函数式的 provider 是编写只有 build 方法的基于类的 provider 的语法糖,因此不能通过 UI 进行修改。
  • 支持 Dart 的所有异步类型(Future,FutureOr 和Stream)
  • 当函数标记为异步时,provider会自动处理错误/加载中状态并暴露为 AsyncValue 。

函数式(无法使用公开方法执行副作用)

基于类 (可以使用公开方法执行副作用)

Sync

函数式

dart 复制代码
@riverpod
String example(ExampleRef ref) {
  return 'foo';
}

基于类

dart 复制代码
@riverpod
class Example extends _$Example {
    @override
    String build() {
        return 'foo';
    }   
    
    // 添加一些改变状态的方法
} 

异步 Future

函数式

dart 复制代码
@riverpod
Future<String> example(ExampleRef ref) async {
    return Future.value('foo');
} 

基于类

dart 复制代码
@riverpod
class Example extends _$Example {
    @override
    Future<String> build() async {
        return Future.value('foo');
    }
    
    // 添加一些改变状态的方法
} 

异步 Stream 函数式

dart 复制代码
@riverpod
Stream<String> example(ExampleRef ref) async* {
    yield 'foo';
} 

基于类

dart 复制代码
@riverpod
class Example extends _$Example {
    @override
    Stream<String> build() async* {
        yield 'foo';
    }
    
    // 添加一些改变状态的方法
} 

启用/禁用 autoDispose (自动清除):

使用代码生成时,provider 默认是 autoDispose(自动清除)的。这意味着当它们没有监听器绑定它们时(ref.watch/ref.listen),它们会自动释放自己。

该默认设定很切合 Riverpod 哲学。不使用代码生成的变量时,默认 autoDispose 是关闭的,这是为了适应客户从 package:provider 进行迁移。

如果想禁用 autoDispose ,可以传递 keepAlive: true 给注解。

dart 复制代码
// AutoDispose 的 provider (keepAlive 默认是 false)
@riverpod
String example1(Example1Ref ref) => 'foo';

// 不会 autoDispose 的 provider
@Riverpod(keepAlive: true)
String example2(Example2Ref ref) => 'foo';

传递参数给 provider(family):

使用代码生成时,就不再需要依赖 family 修饰符向 provider 传递参数了。取而代之的是,provider 的主函数可以接收任意个娄的参数,包括命名参数,可选参数和默认值。

要注意尽管这些参数应该带有一致的 == 。这意味着要么值会被缓存,要么参数应该覆写 == 。

函数式

dart 复制代码
@riverpod
String example(ExampleRef ref, int param1, {String param2 = 'foo', }) {
    return 'Hello $param1 & param2';
} 

基于类

dart 复制代码
@riverpod
class Example extends _$Example {
    @override
    String build(int param1, {String param2 = 'foo',   }) {
        return 'Hello $param1 & param2';
    }
    
    // 添加一些改变状态的方法
} 

从非代码生成的变量迁移:

使用非代码生成的变量时,需要手动决定 provider 的类型。下面的内容是转换为代码生成变量时相应的选项:

Provider

转换前

dart 复制代码
final exampleProvider = Provider.autoDispose<String>(  
    (ref) {  
        return 'foo';  
    },  
);

转换后

dart 复制代码
@riverpod  
String example(ExampleRef ref) {  
    return 'foo';  
}

NotifierProvider

转换前

dart 复制代码
final exampleProvider = NotifierProvider.autoDispose<ExampleNotifier, String>(  
    ExampleNotifier.new,  
);  
  
class ExampleNotifier extends AutoDisposeNotifier<String> {  
    @override  
    String build() {  
        return 'foo';  
    }  

    // 添加一些改变状态的方法
}

转换后

dart 复制代码
@riverpod  
class Example extends _$Example {  
    @override  
    String build() {  
        return 'foo';  
    }  

    // 添加一些改变状态的方法
}

FutureProvider

转换前

dart 复制代码
final exampleProvider =  
FutureProvider.autoDispose<String>((ref) async {  
    return Future.value('foo');  
});

转换后

dart 复制代码
@riverpod  
Future<String> example(ExampleRef ref) async {  
    return Future.value('foo');  
}

StreamProvider

转换前

dart 复制代码
final exampleProvider =  
StreamProvider.autoDispose<String>((ref) async* {  
    yield 'foo';  
});

转换后

dart 复制代码
@riverpod  
Stream<String> example(ExampleRef ref) async* {  
    yield 'foo';  
}

AsyncNotifierProvider

转换前

dart 复制代码
final exampleProvider =  
AsyncNotifierProvider.autoDispose<ExampleNotifier, String>(  
    ExampleNotifier.new,  
);  
  
class ExampleNotifier extends AutoDisposeAsyncNotifier<String> {  
    @override  
    Future<String> build() async {  
        return Future.value('foo');  
    }  

    // 添加一些改变状态的方法
}

转换后

dart 复制代码
@riverpod  
class Example extends _$Example {  
    @override  
    Future<String> build() async {  
        return Future.value('foo');  
    }  

    // 添加一些改变状态的方法
}

StreamNotifierProvider

转换前

dart 复制代码
final exampleProvider =  
StreamNotifierProvider.autoDispose<ExampleNotifier, String>(() {  
    return ExampleNotifier();  
});  
  
class ExampleNotifier extends AutoDisposeStreamNotifier<String> {  
    @override  
    Stream<String> build() async* {  
        yield 'foo';  
    }  

    // 添加一些改变状态的方法
}

转换后

dart 复制代码
@riverpod  
class Example extends _$Example {  
    @override  
    Stream<String> build() async* {  
        yield 'foo';  
    }  

    // 添加一些改变状态的方法
}

相关推荐
Geek_Vison10 小时前
保险APP已经用了Flutter,还能接入小程序吗?
flutter·小程序·uni-app·mpaas·小程序容器
FungLeo10 小时前
成为全栈·Flutter App 篇·go_router 与四 Tab App Shell:保留导航状态、深链和登录回跳
flutter·移动开发·成为全栈·go_router·路由设计·深链接
m0_7381858211 小时前
Flutter 鸿蒙化实战:screen_protector 适配 OpenHarmony,防截屏/防泄露
flutter·华为·harmonyos·鸿蒙
梦想的颜色16 小时前
【编程实战】AI 时代 APP 开发全栈硬核指南:技术选型 + AI 架构 + 模型落地全维度决策
python·flutter·react native·react.js·ai·桌面应用·milvus
yuanlaile1 天前
2026年全新Flutter 跨平台开发全栈学习资料整理
flutter·getx·鸿蒙 next·flutter开发鸿蒙·flutter入门实战·flutter全栈
恋猫de小郭2 天前
Genkit Dart 1.0 发布,Flutter 原生的 AI Agent 终于完整了
android·前端·flutter
第10086个小白2 天前
flutter桌面端打包方式(Inno Setup Compiler)版本
前端·flutter
陆断枫3 天前
Flutter 3.47 动画引擎盘点:Impeller 全平台,和那些"新特性"
flutter
程序员老刘3 天前
Flet 1.0 思路很好,可惜来晚了
flutter·ai编程·客户端
m0_738185823 天前
Flutter 鸿蒙化实战:qr_code_scanner_plus 适配 OpenHarmony,二维码扫描
flutter·华为·harmonyos·鸿蒙