HydratedCubit Brick 完整指南:用 Mason 一键生成可持久化 Cubit
2026/9/23 14:03:15 网站建设 项目流程
  • 前端

【免费下载链接】bloc

A predictable state management library that helps implement the BLoC design pattern

项目地址:https://gitcode.com/gh_mirrors/bl/bloc
点击查看免费下载

HydratedCubit 是 bloc 状态管理库提供的「可持久化 Cubit」形态:它继承了HydratedCubit,通过toJson/fromJson将状态自动持久化到本地存储,App 重启后无需手动恢复状态。本指南以仓库中bricks/hydrated_cubit官方 Brick(Mason 模板)为核心,讲解它的演进历史、模板结构、生成方式与三种代码风格(basic / equatable / freezed),并深入其源码细节,帮助你快速生成、定制并正确落地可持久化的 Cubit 代码。

一、Brick 是什么:hydrated_cubit的定位

bricks/hydrated_cubit是 bloc 仓库中官方维护的一组 Mason Brick,定位是「Generate a new HydratedCubit in Dart. Built for the bloc state management library」(见 README.md)。它服务于以下两类典型场景:

  • 已有基于hydrated_bloc的 Dart / Flutter 项目,需要快速创建带本地持久化能力的 Cubit 骨架;
  • 团队需要统一 Cubit 代码风格,避免每个人手写结构不一致。

与普通cubitBrick 的关键区别在于:生成的类继承自HydratedCubit<T>而非Cubit<T>,因此模板强制要求实现toJsonfromJson两个方法,这也是持久化能力所在。与之配套的还有hydrated_bloc(完整 Bloc)与replay_cubit/replay_bloc等兄弟 Brick,共同组成 bloc 生态的模板体系。

二、版本演进:从 0.1.0 到 0.3.0

Brick 的版本历史完整记录在 CHANGELOG.md 中,其演进脉络清晰反映了模板能力的扩展过程:

版本变更类型内容
0.1.0feat初始发布,支持 basic 风格的 hydrated cubit 生成
0.1.1docs对 README 做小幅更新
0.1.2docsREADME 增加徽章(badges),并使用深色 Logo 变体
0.1.3fix修复 part 指令与 import 的声明问题
0.2.0feat新增equatablefreezed两种风格的模板支持
0.2.1chore更新版权年份与 Logo 图片引用
0.3.0chore升级依赖:mason ^0.1.0,hooks 升级至dart ^3.5.4

可以提炼出三条事实:

  1. 模板能力分层演进:0.1.0 仅支持 basic 风格,0.2.0 才引入 equatable 与 freezed,因此style变量(见下文)的三个取值并非同时出现;
  2. 0.1.3 的 part/imports 修复对应模板文件中part '{{name.snakeCase()}}_state.dart';part of的配对关系,这正是多文件生成 Brick 最容易出错的地方;
  3. 0.3.0 对齐了工具链版本:Brick 依赖mason ^0.1.0,hooks 运行环境要求dart ^3.5.4,使用时需确保本地 mason CLI 与 Dart SDK 满足该约束(可核对 brick.yaml 与 hooks/pubspec.yaml)。

三、使用方式:一条命令生成两个文件

按 README.md 的说明,使用方式极其简单:

mason make hydrated_cubit --name counter --style basic

执行后会在当前目录下生成:

├── counter_cubit.dart └── counter_state.dart

使用前提:本地已安装 Mason CLI)。生成的counter_cubit.dartcounter_state.dart需要放置在你的lib/目录中,并确保pubspec.yaml已引入hydrated_bloc(freezed 风格还需freezed_annotation与 build_runner 配合)。

四、变量与参数:name 与 style 详解

Brick 的输入变量定义在 brick.yaml 中,共两个:

变量类型默认值可选值说明
namestringcounter任意字符串Cubit 类名,命令行交互提示 "Please enter the cubit name."
styleenumbasicbasicequatablefreezed生成模板风格,交互提示 "What is the cubit style?"

命令行交互方式(不传参时按提示输入):

mason make hydrated_cubit # ? Please enter the cubit name. counter # ? What is the cubit style? basic

name在模板中会被 Mason 的变量修饰器进一步处理:{{name.snakeCase()}}用于文件名与 part 指令(如counter_cubit.dart),{{name.pascalCase()}}用于类名(如CounterCubit/CounterState)。

style的分流逻辑并不写在模板的 if 条件里,而是由 hooks/pre_gen.dart 在生成前将枚举值转换为三个布尔变量:

final style = context.vars['style']; context.vars = { ...context.vars, 'use_basic': style == 'basic', 'use_equatable': style == 'equatable', 'use_freezed': style == 'freezed', };

随后模板主文件 {{name.snakeCase()}}_cubit.dart%7D%7D_cubit.dart) 通过 Mustache 区块按需引入对应片段:

{{#use_freezed}}{{> freezed_cubit }}{{/use_freezed}}{{#use_equatable}}{{> equatable_cubit }}{{/use_equatable}}{{#use_basic}}{{> basic_cubit }}{{/use_basic}}

{{name.snakeCase()}}_state.dart%7D%7D_state.dart) 采用相同的三段式分发。这种「pre_gen 预计算变量 + 主文件按片段拼接」的模式,是 Mason 多风格 Brick 的通用最佳实践,便于后续新增风格时只需添加片段文件与 hook 分支。

五、三种生成风格与底层实现剖析

5.1 basic:最小可持久化 Cubit

basic 风格的 Cubit 模板见 {{~ basic_cubit }}:

import 'package:hydrated_bloc/hydrated_bloc.dart'; part 'counter_state.dart'; class CounterCubit extends HydratedCubit<CounterState> { CounterCubit() : super(const CounterState()); @override Map<String, dynamic> toJson(CounterState state) { // TODO: implement toJson } @override CounterState fromJson(Map<String, dynamic> json) { // TODO: implement fromJson } }

对应状态模板见 {{~ basic_state }}:

part of 'counter_cubit.dart'; class CounterState { const CounterState(); }

两个要点:

  • 继承自HydratedCubit<T>:这是与普通 Cubit 模板的核心差异。HydratedCubit位于仓库的 packages/hydrated_bloc 包中,它在每次emit新状态后自动调用toJson序列化并写入存储,在 App 启动恢复时调用fromJson反序列化,从而在原生状态管理之上叠加了透明的本地持久化能力;
  • 模板刻意留白toJson/fromJson// TODO: implement占位,交由开发者按自己的状态模型填充,因为 Brick 无法预知业务状态的字段结构。

5.2 equatable:带值比较的持久化状态

equatable 风格的 Cubit 与 basic 几乎一致(仅 import 增加equatable),关键差异在状态模板 {{~ equatable_state }}:

part of 'counter_cubit.dart'; class CounterState extends Equatable { const CounterState(); @override List<Object> get props => []; }

props目前为空列表,生成后需按业务字段补充,例如:

class CounterState extends Equatable { const CounterState({this.count = 0}); final int count; @override List<Object> get props => [count]; }

Equatable的价值在于:当状态值未变化时,==hashCode判定相等,bloc 生态可据此跳过冗余 rebuild,从而减少不必要的 Widget 重建与状态派发。

5.3 freezed:代码生成与不可变状态

freezed 风格引入 Dart 代码生成,Cubit 模板见 {{~ freezed_cubit }}:

import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; part 'counter_state.dart'; part 'counter_cubit.freezed.dart'; class CounterCubit extends HydratedCubit<CounterState> { CounterCubit() : super(const CounterState.initial()); @override Map<String, dynamic> toJson(CounterState state) { // TODO: implement toJson } @override CounterState fromJson(Map<String, dynamic> json) { // TODO: implement fromJson } }

状态模板见 {{~ freezed_state }}:

part of 'counter_cubit.dart'; @freezed class CounterState with _$CounterState { const factory CounterState.initial() = _Initial; }

两个值得注意的细节:

  • 多了一个part '{{name.snakeCase()}}_cubit.freezed.dart';:这是 freezed 代码生成产生的文件,必须在运行build_runner后才会出现,因此在 CI/团队协作中需先执行生成命令再编译;
  • 初始状态由命名构造CounterState.initial()提供:freezed 的 union/sealed 风格让后续扩展多个状态分支(如loadingerrorloaded)变得非常自然,例如:
@freezed class CounterState with _$CounterState { const factory CounterState.initial() = _Initial; const factory CounterState.value(int count) = _Value; }

三种风格的选择建议:追求最小依赖选 basic;需要频繁比较状态是否变化(配合BlocBuilder/BlocSelector优化重建)选 equatable;需要不可变、可模式匹配的多分支状态且团队已接受 build_runner 工作流选 freezed。此建议由 bricks/cubit(同样支持三种风格)及 packages/flutter_bloc 的公开设计推断得出。

六、与 bloc 仓库生态的联动

hydrated_cubitBrick 在整个 bloc 仓库中并非孤立的模板,理解其上下文有助于正确使用:

  • hydrated_bloc(packages/hydrated_bloc)提供HydratedCubit基类与存储抽象,是生成的代码能运行的运行时前提;其示例(example)与测试(test)展示了持久化行为如何被验证;
  • 兄弟 Brickbricks/cubit(无持久化的纯 Cubit)、bricks/hydrated_bloc(持久化 Bloc)、bricks/replay_cubit/bricks/replay_bloc(可回放状态流)、bricks/flutter_bloc_feature(面向 Flutter 的完整 feature 脚手架)。它们共享name+style的变量设计,学习hydrated_cubit后可以零成本迁移到其他 Brick;
  • hooks 机制pre_gen.dart是 Mason 的生成前钩子,pubspec.yaml(hooks/pubspec.yaml)声明了 hooks 自身的依赖与 SDK 约束,这与 0.3.0 中「升级 hooks 到 dart ^3.5.4」的变更相互印证。

七、常见问题与最佳实践

  1. 为什么toJson返回Map<String, dynamic>而不是直接存对象?因为HydratedCubit的存储层(默认基于本地文件/存储抽象)以 JSON 可序列化的 Map 为持久化单元。返回 null 时表示该状态不需持久化(例如某些瞬时状态可以跳过存储)。

  2. partpart of必须配对:Cubit 文件中part 'xxx_state.dart';,状态文件中part of 'xxx_cubit.dart';。文件名由snakeCase()统一派生,这正是 0.1.3 版本修复的重点,改动文件名时务必保持两者一致。

  3. freezed 风格编译报错找不到freezed.dart:需要先运行代码生成:

    dart run build_runner build

    建议将其纳入 CI 流程。

  4. 持久化不生效的排查路径:确认HydratedBloc.storage已在main()中初始化(参考hydrated_bloc包文档);确认状态类字段都已纳入toJson/fromJson;确认使用了HydratedCubit而不是普通Cubit

  5. 多文件生成的命名一致性:所有模板文件都基于同一个name变量,通过snakeCase()/pascalCase()派生文件名与类名,因此只要name取规范的小驼峰/小写下划线形式,生成的文件与类即可保证互相匹配。

八、小结

bricks/hydrated_cubit是一个「小而精」的官方 Brick:两条变量(namestyle)、两个输出文件、三种代码风格,再叠加HydratedCubit的持久化语义与pre_genhook 的分流逻辑,构成了 bloc 仓库中可持久化 Cubit 的标准生成入口。从 CHANGELOG.md 的演进可以看到它的成熟路径,而从 brick.yaml 与brick模板则能完整复现它的工作机制。若你想进一步定制(如增加新的风格、补充默认的 JSON 序列化实现),以本 Brick 为起点修改是成本最低的路径。

  • 前端

【免费下载链接】bloc

A predictable state management library that helps implement the BLoC design pattern

项目地址:https://gitcode.com/gh_mirrors/bl/bloc
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询