Clean Architecture中如何发送POST请求?Bloc场景传参需求
问题描述
我目前在项目中通过BlocBuilder在构建阶段发起GET请求(无需绑定点击事件),后续自动填充模型数据,现有代码如下:
BlocProvider初始化代码
return BlocProvider<BannerCubit>( create: (context) => sl<BannerCubit>()..loadBanner(), child: Scaffold(body: BannerCarousels(),), );
BannerCarousel组件代码
Widget build(BuildContext context) { return BlocBuilder<BannerCubit, BannerState>( builder: (context, state) { List<BannerEntity> banner = []; if(state is BannerLoading) { return _loadingIndicator(); } else if (state is BannerLoaded) { banner = state.bannerList; } print('The following data for the banner came to me from the server: $banner'); return CarouselSlider.builder( // 轮播逻辑... ); } ); }
数据源层GET请求代码
Future<List<ProductsModel>> getAllProducts() async { final response = await client.get( Uri.parse(ConfigUrl.home), headers: {'Content-Type': 'application/json'} ); if(response.statusCode == 200) { final products = json.decode(response.body); print('For banner: $products'); return (products['products'] as List).map((e) => ProductsModel.fromJson(e)).toList(); } else { throw ServerException(); } }
现在我的疑问是:如何将TextFormField控制器中的数据传入POST请求体?是否需要在触发请求的按钮组件中直接构建请求?
注:我刚学习Clean Architecture,不清楚如何发送POST请求、填充请求体及解析响应,希望能看到适合新手的示例代码。
解决方案
遵循Clean Architecture和Bloc的分层原则,不要在按钮组件里直接写请求逻辑,应该把请求逻辑放在数据源/仓库层,状态流转交给Cubit处理。下面是完整的新手友好示例:
步骤1:UI层管理输入控制器
在页面组件中定义TextFormField的控制器,用于获取用户输入:
class LoginPage extends StatefulWidget { @override _LoginPageState createState() => _LoginPageState(); } class _LoginPageState extends State<LoginPage> { final _emailController = TextEditingController(); final _passwordController = TextEditingController(); @override void dispose() { _emailController.dispose(); _passwordController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: Padding( padding: EdgeInsets.all(16), child: Column( children: [ TextFormField( controller: _emailController, decoration: InputDecoration(hintText: '邮箱'), keyboardType: TextInputType.emailAddress, ), TextFormField( controller: _passwordController, decoration: InputDecoration(hintText: '密码'), obscureText: true, ), ElevatedButton( onPressed: () { // 点击时触发Cubit的请求方法,传入输入值 context.read<LoginCubit>().login( email: _emailController.text.trim(), password: _passwordController.text.trim(), ); }, child: Text('登录'), ), // 用BlocBuilder处理请求状态 BlocBuilder<LoginCubit, LoginState>( builder: (context, state) { if (state is LoginLoading) { return CircularProgressIndicator(); } else if (state is LoginSuccess) { return Text('登录成功!用户ID:${state.userId}'); } else if (state is LoginFailure) { return Text('登录失败:${state.errorMessage}'); } return SizedBox.shrink(); }, ), ], ), ), ); } }
步骤2:创建处理POST请求的Cubit
定义Cubit和对应的状态,处理请求的状态流转:
// 登录状态基类 abstract class LoginState {} class LoginInitial extends LoginState {} class LoginLoading extends LoginState {} class LoginSuccess extends LoginState { final String userId; LoginSuccess(this.userId); } class LoginFailure extends LoginState { final String errorMessage; LoginFailure(this.errorMessage); } // 登录Cubit class LoginCubit extends Cubit<LoginState> { final AuthRepository authRepository; // 依赖注入仓库 LoginCubit(this.authRepository) : super(LoginInitial()); Future<void> login({required String email, required String password}) async { emit(LoginLoading()); try { final userId = await authRepository.login(email: email, password: password); emit(LoginSuccess(userId)); } catch (e) { emit(LoginFailure(e.toString())); } } }
步骤3:在数据源层实现POST请求
在数据源类中添加POST请求方法,构建并填充请求体:
// 数据源层 class AuthRemoteDataSource { final Dio client; // 或你使用的http.Client AuthRemoteDataSource(this.client); Future<String> login(String email, String password) async { final response = await client.post( Uri.parse(ConfigUrl.login), headers: {'Content-Type': 'application/json'}, // 将输入参数转为JSON作为请求体 data: json.encode({ 'email': email, 'password': password, }), ); if (response.statusCode == 200) { final data = json.decode(response.body); return data['user_id']; // 解析响应中的目标数据 } else { throw ServerException(); // 自定义异常类 } } } // 仓库层(Clean Architecture中间层) class AuthRepository { final AuthRemoteDataSource remoteDataSource; AuthRepository(this.remoteDataSource); Future<String> login({required String email, required String password}) async { return await remoteDataSource.login(email, password); } }
步骤4:注入依赖并初始化Cubit
和你之前的BannerCubit一样,用get_it(sl)注入依赖,在页面外层包裹BlocProvider:
// 依赖注入初始化 sl.registerLazySingleton<AuthRemoteDataSource>(() => AuthRemoteDataSource(sl())); sl.registerLazySingleton<AuthRepository>(() => AuthRepository(sl())); sl.registerFactory<LoginCubit>(() => LoginCubit(sl())); // 页面路由中 return BlocProvider<LoginCubit>( create: (context) => sl<LoginCubit>(), child: LoginPage(), );
关键说明
- 分层职责清晰:UI层仅负责收集输入、触发事件、展示状态;Cubit处理业务逻辑和状态流转;数据源层负责实际网络请求。
- 请求体填充:通过
json.encode()将输入参数转为JSON格式,作为POST请求的data参数传入。 - 响应解析:请求成功后从响应体中解析目标数据,通过Cubit将状态同步给UI层。
- 异常处理:请求失败时抛出自定义异常,Cubit捕获后转为失败状态,UI层展示错误信息。
内容的提问来源于stack exchange,提问作者Daniil
相关产品推荐
相关产品推荐

