如何在Clean Architecture+Bloc的Flutter登录实现中正确传递参数?
问题描述
我正在学习基于Clean Architecture的Flutter开发,登录功能需要向API传递company、username、pin三个参数,当前使用Flutter Bloc和Dio框架。目前代码通过const.dart文件中定义的常量参数可正常运行,但我希望在点击登录按钮时,将三个TextFormField的输入值作为动态参数传递给LoginRepositoryImpl中的API请求,同时遵循Clean Architecture原则。
当前相关代码
登录按钮
_buildLoginButton(context) { return OutlinedButton( onPressed: () { // Dispatch the GetLoginDetails event with the login details if (formKey.currentState!.validate()) { BlocProvider.of<RemoteLoginDetailsBloc>(context).add( GetLoginDetails( companyController.text.trim(), usernameController.text.trim(), pinController.text.trim(), ), ); } }, child: const Text("Login"), ); }
RemoteLoginBloc
class RemoteLoginDetailsBloc extends Bloc<RemoteLoginDetailsEvent, RemoteLoginState> { final GetLoginDetailsUseCase _getLoginUseCase; RemoteLoginDetailsBloc(this._getLoginUseCase) : super(const RemoteLoginDetailsLoading()) { on<GetLoginDetails>(onGetLoginDetails); } void onGetLoginDetails( GetLoginDetails event, Emitter<RemoteLoginState> emit) async { final dataState = await _getLoginUseCase(); if (dataState is DataSuccess && dataState.data!.isNotEmpty) { emit(RemoteLoginDone(dataState.data!)); } if (dataState is DataFailed) { debugPrint( 'Data Failed Error: ${dataState.error!.message} $loginAPIBaseURL'); emit(RemoteLoginError(dataState.error!)); } } }
RemoteLoginState
abstract class RemoteLoginState extends Equatable { final List<LoginEntity>? loginDetails; final DioException? error; const RemoteLoginState({this.loginDetails, this.error}); @override List<Object?> get props => [loginDetails!, error!]; } class RemoteLoginDetailsLoading extends RemoteLoginState { const RemoteLoginDetailsLoading(); } class RemoteLoginDone extends RemoteLoginState { const RemoteLoginDone(List<LoginEntity> login) : super(loginDetails: login); } class RemoteLoginError extends RemoteLoginState { const RemoteLoginError(DioException error) : super(error: error); }
RemoteLoginEvent
abstract class RemoteLoginDetailsEvent { const RemoteLoginDetailsEvent(); } class GetLoginDetails extends RemoteLoginDetailsEvent { GetLoginDetails(); }
GetLoginDetailsUseCase
class GetLoginDetailsUseCase implements UseCase<DataState<List<LoginEntity>>, void> { final LoginRepository _loginRepository; GetLoginDetailsUseCase(this._loginRepository); @override Future<DataState<List<LoginEntity>>> call({void params}) { return _loginRepository.getLogonDetails(); } }
LoginRepository
abstract class LoginRepository { // API Methods Future<DataState<List<LoginEntity>>> getLogonDetails(); }
LoginRepositoryImpl
class LoginRepositoryImpl implements LoginRepository { //final LoginDatabase _loginDatabase; final LoginApiService _loginApiService; LoginRepositoryImpl(this._loginApiService); @override Future<DataState<List<LoginModel>>> getLogonDetails() async { try { final httpResponse = await _loginApiService.getLogonDetails( apiKey: telMeApiKey, lcomp: lcomp, luser: luser, lpin: lpin, ); // Log the request and response for debugging debugPrint('Request: ${httpResponse.response.requestOptions.uri}'); debugPrint('Response status code: ${httpResponse.response.statusCode}'); if (httpResponse.response.statusCode == HttpStatus.ok) { return DataSuccess(httpResponse.data); } else { return DataFailed(DioException( error: httpResponse.response.statusMessage, response: httpResponse.response, type: DioExceptionType.badResponse, requestOptions: httpResponse.response.requestOptions, )); } } on DioException catch (e) { debugPrint('TelMeLogonRequest - Error:$e'); return DataFailed(e); } } }
修改步骤(遵循Clean Architecture)
1. 更新RemoteLoginEvent,让GetLoginDetails携带登录参数
给事件添加参数并继承Equatable,确保Bloc能正确识别事件差异:
abstract class RemoteLoginDetailsEvent extends Equatable { const RemoteLoginDetailsEvent(); @override List<Object?> get props => []; } class GetLoginDetails extends RemoteLoginDetailsEvent { final String company; final String username; final String pin; const GetLoginDetails(this.company, this.username, this.pin); @override List<Object?> get props => [company, username, pin]; }
2. 定义参数实体并修改GetLoginDetailsUseCase
在Domain层定义参数封装类,同时更新UseCase的参数类型:
// 登录参数实体(放在domain/entities目录下) class LoginParams { final String company; final String username; final String pin; const LoginParams({ required this.company, required this.username, required this.pin, }); } // 修改UseCase class GetLoginDetailsUseCase implements UseCase<DataState<List<LoginEntity>>, LoginParams> { final LoginRepository _loginRepository; GetLoginDetailsUseCase(this._loginRepository); @override Future<DataState<List<LoginEntity>>> call({required LoginParams params}) { return _loginRepository.getLogonDetails(params); } }
3. 更新LoginRepository抽象方法
让抽象层支持接收登录参数:
abstract class LoginRepository { // API Methods Future<DataState<List<LoginEntity>>> getLogonDetails(LoginParams params); }
4. 修改LoginRepositoryImpl,传递动态参数到API
从参数实体中取值替换原有的常量:
class LoginRepositoryImpl implements LoginRepository { final LoginApiService _loginApiService; LoginRepositoryImpl(this._loginApiService); @override Future<DataState<List<LoginModel>>> getLogonDetails(LoginParams params) async { try { final httpResponse = await _loginApiService.getLogonDetails( apiKey: telMeApiKey, lcomp: params.company, luser: params.username, lpin: params.pin, ); debugPrint('Request: ${httpResponse.response.requestOptions.uri}'); debugPrint('Response status code: ${httpResponse.response.statusCode}'); if (httpResponse.response.statusCode == HttpStatus.ok) { return DataSuccess(httpResponse.data); } else { return DataFailed(DioException( error: httpResponse.response.statusMessage, response: httpResponse.response, type: DioExceptionType.badResponse, requestOptions: httpResponse.response.requestOptions, )); } } on DioException catch (e) { debugPrint('TelMeLogonRequest - Error:$e'); return DataFailed(e); } } }
5. 更新RemoteLoginDetailsBloc,传递参数给UseCase
从事件中提取参数,封装后传给UseCase:
void onGetLoginDetails( GetLoginDetails event, Emitter<RemoteLoginState> emit) async { final dataState = await _getLoginUseCase( params: LoginParams( company: event.company, username: event.username, pin: event.pin, ), ); if (dataState is DataSuccess && dataState.data!.isNotEmpty) { emit(RemoteLoginDone(dataState.data!)); } if (dataState is DataFailed) { debugPrint( 'Data Failed Error: ${dataState.error!.message} $loginAPIBaseURL'); emit(RemoteLoginError(dataState.error!)); } }
说明
以上修改严格遵循Clean Architecture分层规则:
- Presentation层:负责收集用户输入,触发Bloc事件
- Domain层:定义参数实体和业务规则,依赖抽象而非具体实现
- Data层:接收Domain层参数,执行具体API请求
内容的提问来源于stack exchange,提问作者RJASSI98
相关产品推荐
相关产品推荐

