Flutter中Stream与私有StreamController的最佳命名规范是什么
几乎所有用RxDart做状态/事件处理的Flutter开发者,都碰到过你说的这个命名冗余问题,早期大家照着网上的老教程写,全是xxxController配xxxStream的写法,一个流要写两遍带后缀的名字,样板代码多到离谱。
目前生态里认可度比较高的方案主要有3种,不同团队根据技术栈选就行,没有绝对的对错:
1. 最通用的官方对齐写法
这是目前普及率最高的方案,Dart官方示例、Bloc、Provider等主流生态包的demo里基本都用这套:
- 私有写入端(也就是
StreamController/BehaviorSubject/PublishSubject这类可控的流入口)统一保留_controller后缀,这个约定不要省,类内部写代码的时候一眼就能认出这是流的写入入口,不会误调用close或者乱add数据。 - 对外暴露的只读流,直接用业务语义命名,完全不需要加
Stream后缀。
// 示例 final BehaviorSubject<UserInfo> _userInfoController; // 对外暴露直接用语义名,调用的时候顺很多 ValueStream<UserInfo> get userInfo => _userInfoController; final PublishSubject<AppException> _errorController; Stream<AppException> get error => _errorController;
我接触过的中大型Flutter项目里,七成以上都用这套,学习成本最低,新人进来不需要额外记约定,看官方文档就能顺下来,冗余也比老写法少很多。
2. 响应式团队常用的$后缀写法
这套是从前端RxJS生态传过来的,全栈做响应式开发、团队成员都熟悉Rx概念的项目用得很多:
- 对外暴露的可订阅流,统一在名称末尾加
$后缀,不用看类型就能一眼认出这是个流,和普通变量、单次返回的Future做区分。 - 私有Controller要么保留
_controller后缀,要么简单用下划线加业务名就行。
// 示例 final BehaviorSubject<String> _nicknameController; Stream<String> get nickname$ => _nicknameController; // 单次获取的Future正常命名,不加后缀 Future<String> getNickname() async {}
这套写法在全量用RxDart、GetX、MobX的项目里接受度很高,辨识度强,写起来也简洁。注意不要把$加在私有Controller上,这个后缀只用来标记对外暴露的订阅流就好。
3. 状态库生态衍生的watch前缀写法
这套是随着Provider、Riverpod这些状态库普及开的,库本身的API就用watch做状态监听的方法名,所以很多用这类库的团队会沿用这个命名规则:
- 持续订阅的流方法统一用
watchXxx做前缀,语义非常明确,就是用来持续监听状态变化的。 - 单次获取数据的Future方法对应使用
getXxx前缀,两者区分度很高,新人不会搞混。
// 示例 Stream<UserInfo> watchUserInfo() => _userInfoController; Future<UserInfo> getUserInfo() async {}
全量用Riverpod的项目基本都偏向这套,和生态自带的代码生成规则、API风格完全对齐,写起来一致性很强。
避坑提醒:别为了省代码把私有Controller和对外暴露的流写成只差一个下划线的同名形式,比如
final _user = BehaviorSubject<User>(); Stream<User> get user => _user;,这种写法看起来最简洁,但是类内部逻辑复杂的时候,非常容易误操作私有Controller,排查问题的成本比省下来的那点后缀代码高多了。
内容的提问来源于stack exchange,提问作者dudeck

