You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

当前页面BLOC Provider无创建类时,如何基于BLOC模式实现POST请求?

基于Bloc模式实现登录POST请求

1. 添加依赖

先在pubspec.yaml中引入所需依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_bloc: ^8.1.3
  http: ^1.1.0

执行flutter pub get完成依赖安装。

2. 创建Bloc核心组件

2.1 定义登录事件

创建login_event.dart文件:

part of 'login_bloc.dart';

abstract class LoginEvent extends Equatable {
  const LoginEvent();

  @override
  List<Object> get props => [];
}

class LoginSubmitted extends LoginEvent {
  final String username;
  final String password;

  const LoginSubmitted({required this.username, required this.password});

  @override
  List<Object> get props => [username, password];
}

2.2 定义登录状态

创建login_state.dart文件:

part of 'login_bloc.dart';

abstract class LoginState extends Equatable {
  const LoginState();

  @override
  List<Object> get props => [];
}

class LoginInitial extends LoginState {}

class LoginLoading extends LoginState {}

class LoginSuccess extends LoginState {
  final String token;

  const LoginSuccess({required this.token});

  @override
  List<Object> get props => [token];
}

class LoginFailure extends LoginState {
  final String error;

  const LoginFailure({required this.error});

  @override
  List<Object> get props => [error];
}

2.3 实现LoginBloc逻辑

创建login_bloc.dart文件,处理POST请求和状态流转:

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

part 'login_event.dart';
part 'login_state.dart';

class LoginBloc extends Bloc<LoginEvent, LoginState> {
  final http.Client httpClient;

  LoginBloc({required this.httpClient}) : super(LoginInitial()) {
    on<LoginSubmitted>(_onLoginSubmitted);
  }

  Future<void> _onLoginSubmitted(
    LoginSubmitted event,
    Emitter<LoginState> emit,
  ) async {
    emit(LoginLoading());
    try {
      // 替换为你的实际登录接口地址
      final response = await httpClient.post(
        Uri.parse('https://your-api-domain.com/login'),
        headers: {'Content-Type': 'application/json'},
        body: jsonEncode({
          'username': event.username,
          'password': event.password,
        }),
      );

      if (response.statusCode == 200) {
        final data = jsonDecode(response.body);
        emit(LoginSuccess(token: data['token']));
      } else {
        final errorMsg = jsonDecode(response.body)['message'] ?? '登录失败,请重试';
        emit(LoginFailure(error: errorMsg));
      }
    } catch (e) {
      emit(LoginFailure(error: '网络连接异常,请检查网络'));
    }
  }
}

3. 集成Bloc到登录页面

修改原main.dart,通过BlocProvider注入Bloc实例,并更新LoginPage的UI逻辑:

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:http/http.dart' as http;
import 'login_bloc.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: false,
      ),
      home: BlocProvider(
        create: (context) => LoginBloc(httpClient: http.Client()),
        child: const LoginPage(),
      ),
    );
  }
}

class LoginPage extends StatefulWidget {
  const LoginPage({super.key});

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  TextEditingController userName = TextEditingController();
  TextEditingController password = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: BlocConsumer<LoginBloc, LoginState>(
        listener: (context, state) {
          // 监听状态变化处理副作用
          if (state is LoginSuccess) {
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text('登录成功!Token: ${state.token}')),
            );
            // 这里可以添加跳转到主页的逻辑
          } else if (state is LoginFailure) {
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text(state.error), backgroundColor: Colors.red),
            );
          }
        },
        builder: (context, state) {
          return Container(
            width: MediaQuery.of(context).size.width,
            padding: const EdgeInsets.only(left: 20, right: 20),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const Text("User Name"),
                TextField(
                  controller: userName,
                  enabled: state is! LoginLoading, // 加载时禁用输入框
                ),
                const Padding(
                  padding: EdgeInsets.only(top: 20),
                  child: Text("Password"),
                ),
                TextField(
                  controller: password,
                  obscureText: true,
                  enabled: state is! LoginLoading,
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Padding(
                      padding: const EdgeInsets.only(top: 20),
                      child: ElevatedButton(
                        onPressed: state is LoginLoading
                            ? null // 加载时禁用按钮
                            : () {
                                // 触发登录事件
                                context.read<LoginBloc>().add(
                                      LoginSubmitted(
                                        username: userName.text,
                                        password: password.text,
                                      ),
                                    );
                              },
                        child: state is LoginLoading
                            ? const CircularProgressIndicator(color: Colors.white)
                            : const Text('Login'),
                      ),
                    ),
                  ],
                )
              ],
            ),
          );
        },
      ),
    );
  }
}

核心逻辑说明

  • 事件触发:点击登录按钮时,通过context.read<LoginBloc>().add(...)发送登录事件
  • 状态管理:Bloc内部处理POST请求,根据接口响应更新状态(加载中/成功/失败)
  • UI响应:BlocConsumer同时处理UI渲染(比如加载时显示进度条、禁用交互)和状态变化的副作用(提示消息、页面跳转)

内容的提问来源于stack exchange,提问作者Rajasekaran K

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 16:15:22