Flutter Bloc场景下如何实现通用输入组件与业务Bloc的解耦复用
解决方案
方案1:回调注入(最推荐,完全解耦)
把通用输入组件改成纯UI组件,所有业务逻辑(事件提交、校验)都由父层传入,组件内部不依赖任何Bloc类型。
改造后的通用输入组件代码
// 完全无Bloc依赖,纯UI组件 Widget emailInputField({ required String? errorText, required Function(String value) onChanged, String labelText = 'Email' }) { return TextFormField( validator: (value) => errorText, onChanged: onChanged, decoration: InputDecoration( labelText: labelText, errorText: errorText ), ... ); }
登录页调用示例
BlocBuilder<LoginBloc, LoginState>( builder:(context, state){ return emailInputField( errorText: state.isValidEmail ? null : 'Please input a valid email', onChanged: (value) => context.read<LoginBloc>().add(LoginUsernameChanged(username: value)), ); } )
注册页调用示例
BlocBuilder<RegisterBloc, RegisterState>( builder:(context, state){ return emailInputField( errorText: state.isValidEmail ? null : 'This email has been registered', onChanged: (value) => context.read<RegisterBloc>().add(RegisterEmailChanged(email: value)), ); } )
这个方案的好处是通用组件100%和业务逻辑解耦,后续不管加什么新的表单页面都可以直接复用,不需要修改组件本身。
方案2:泛型+接口抽象(适合表单逻辑相似度极高的场景)
先抽象所有认证类Bloc的通用行为接口:
abstract class IAuthFormBloc { void onEmailChanged(String email); void onPasswordChanged(String password); }
然后让各个页面的Bloc实现这个接口:
class LoginBloc extends Bloc<BaseEvent, LoginState> implements IAuthFormBloc { LoginBloc() : super(LoginState()); @override void onEmailChanged(String email) { add(LoginUsernameChanged(username: email)); } @override void onPasswordChanged(String password) { add(LoginPasswordChanged(password: password)); } @override Stream<LoginState> mapEventToState(BaseEvent event) async*{ yield* event.handleEvent(state); } }
通用组件用泛型限定Bloc类型:
Widget emailInputField<B extends IAuthFormBloc, S>(BuildContext context, S state, bool Function(S) isValidEmail) { return TextFormField( validator: (value) => isValidEmail(state) ? null : 'Please input a valid email', onChanged: (value) => context.read<B>().onEmailChanged(value), decoration: const InputDecoration(labelText: 'Email'), ); }
调用时指定泛型即可:
BlocBuilder<LoginBloc, LoginState>( builder:(context, state){ return emailInputField<LoginBloc, LoginState>( context, state, (state) => state.isValidEmail ); } )
对「统一Bloc处理输入逻辑」方案的评估
这个方案在所有表单输入校验规则完全一致的场景下是可行的,能进一步减少重复代码。但需要注意以下局限性:
- 如果后续不同页面需要增加特殊校验逻辑(比如注册页校验邮箱是否已存在、修改密码页校验新旧密码不能一致),统一Bloc会逐渐堆积不同页面的业务逻辑,违反单一职责原则
- 如果不同表单的字段结构差异较大(比如修改密码页需要旧密码、新密码、确认密码三个字段,而登录页只需要账号密码),统一Bloc的状态会出现大量冗余字段,维护成本升高
如果你的所有认证类表单的输入规则、字段结构都高度一致,可以用这个方案,否则更推荐上面的回调注入方案。
内容的提问来源于stack exchange,提问作者Gicu Mironica
相关产品推荐
相关产品推荐

