#first commit
This commit is contained in:
77
lib/features/auth/presentation/bloc/auth_cubit.dart
Normal file
77
lib/features/auth/presentation/bloc/auth_cubit.dart
Normal file
@@ -0,0 +1,77 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:maibu_satabot_v2/core/consts/tcp_consts.dart';
|
||||
import 'package:maibu_satabot_v2/core/domain/entities/user_entity.dart';
|
||||
import 'package:maibu_satabot_v2/core/network/net_message_dispatcher.dart';
|
||||
|
||||
import '../../../../core/app/app_cubit.dart';
|
||||
import '../../../../core/network/tcp/tcp_client.dart';
|
||||
import '../../../../core/storage/user_storage.dart';
|
||||
import 'auth_state.dart';
|
||||
|
||||
/// emit做的事情:
|
||||
/// 1.修改状态标志:通过改变类的类型(从 Initial 变成 Authenticated)
|
||||
/// 2.携带数据:把 user 对象塞进了状态里,让外部能拿到
|
||||
/// 3.发送通知:自动触发监听(如 GoRouter 的刷新)。
|
||||
class AuthCubit extends Cubit<AuthState> {
|
||||
final UserStorage storage;
|
||||
final TcpClient tcp;
|
||||
final AppCubit appCubit;
|
||||
final NetMessageDispatcher dispatcher;
|
||||
StreamSubscription? _kickOutSub; // 新增:用于管理监听生命周期
|
||||
|
||||
AuthCubit(this.storage, this.tcp, this.appCubit, this.dispatcher)
|
||||
: super(AuthInitial()) {
|
||||
// Cubit 一启动就开始监听 TCP 的“自动逻辑”
|
||||
_listenToAuthResponse();
|
||||
}
|
||||
|
||||
/// App 启动时检查本地缓存
|
||||
Future<void> appStarted() async {
|
||||
final user = await storage.getUser();
|
||||
if (user != null) {
|
||||
// 1. 建立 TCP 连接 (远程控制)
|
||||
tcp.connect(host: TCPConsts.TCP_IP, port: TCPConsts.TCP_PORT);
|
||||
// 2. 同步全局 App 状态
|
||||
appCubit.setAuth(user);
|
||||
// 3. 进入已登录状态
|
||||
emit(AuthAuthenticated(user));
|
||||
} else {
|
||||
emit(AuthUnauthenticated());
|
||||
}
|
||||
}
|
||||
|
||||
/// 当 HTTP 登录/注册成功后调用
|
||||
Future<void> loginSuccess(UserEntity user) async {
|
||||
await storage.saveUser(user);
|
||||
tcp.connect(host: TCPConsts.TCP_IP, port: TCPConsts.TCP_PORT);
|
||||
appCubit.setAuth(user);
|
||||
emit(AuthAuthenticated(user));
|
||||
}
|
||||
|
||||
/// 退出登录 (主动或被动)
|
||||
Future<void> logout() async {
|
||||
await storage.deleteUser();
|
||||
tcp.disconnect();
|
||||
appCubit.clearAuth();
|
||||
emit(AuthUnauthenticated());
|
||||
}
|
||||
|
||||
/// TCP 指令监听
|
||||
void _listenToAuthResponse() {
|
||||
_kickOutSub?.cancel(); // 防止重复监听
|
||||
|
||||
// 假设 0x12 是踢下线或多设备登录提醒
|
||||
_kickOutSub = dispatcher.onJsonMessage(0x12).listen((json) {
|
||||
// 如果后端发来指令确认需要退出
|
||||
logout();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
_kickOutSub?.cancel(); // 销毁 Cubit 时关闭监听
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
12
lib/features/auth/presentation/bloc/auth_state.dart
Normal file
12
lib/features/auth/presentation/bloc/auth_state.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'package:maibu_satabot_v2/core/domain/entities/user_entity.dart';
|
||||
|
||||
abstract class AuthState {}
|
||||
|
||||
class AuthInitial extends AuthState {}
|
||||
|
||||
class AuthAuthenticated extends AuthState {
|
||||
final UserEntity user;
|
||||
AuthAuthenticated(this.user);
|
||||
}
|
||||
|
||||
class AuthUnauthenticated extends AuthState {}
|
||||
24
lib/features/auth/presentation/bloc/login_bloc.dart
Normal file
24
lib/features/auth/presentation/bloc/login_bloc.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../domain/usecases/login_usecase.dart';
|
||||
import 'login_event.dart';
|
||||
import 'login_state.dart';
|
||||
|
||||
class LoginBloc extends Bloc<LoginEvent, LoginState> {
|
||||
final LoginUseCase loginUseCase;
|
||||
|
||||
LoginBloc(this.loginUseCase) : super(LoginInitial()) {
|
||||
on<LoginSubmitted>((event, emit) async {
|
||||
emit(LoginLoading());
|
||||
try {
|
||||
final user = await loginUseCase(
|
||||
LoginParams(event.username, event.password, event.sourceType),
|
||||
);
|
||||
print('✅ Got user: $user');
|
||||
emit(LoginSuccess(user));
|
||||
} catch (e) {
|
||||
emit(LoginFailure(e.toString()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
29
lib/features/auth/presentation/bloc/login_cubit.dart
Normal file
29
lib/features/auth/presentation/bloc/login_cubit.dart
Normal file
@@ -0,0 +1,29 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../domain/usecases/login_usecase.dart';
|
||||
import 'auth_cubit.dart';
|
||||
import 'login_state.dart';
|
||||
|
||||
class LoginCubit extends Cubit<LoginState> {
|
||||
final LoginUseCase loginUseCase;
|
||||
final AuthCubit authCubit;
|
||||
|
||||
LoginCubit(this.loginUseCase, this.authCubit) : super(LoginInitial());
|
||||
|
||||
/// Login
|
||||
/// @param username 用户名
|
||||
/// @param password 密码
|
||||
/// @param sourceType 登录来源: 1:web; 2:mobile; 3:mini_programs; 4:pc
|
||||
Future<void> login(String username, String password, sourceType) async {
|
||||
emit(LoginLoading());
|
||||
try {
|
||||
final resultUser = await loginUseCase.call(
|
||||
LoginParams(username, password, sourceType),
|
||||
);
|
||||
authCubit.loginSuccess(resultUser);
|
||||
emit(LoginSuccess(resultUser));
|
||||
} catch (e) {
|
||||
emit(LoginFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
9
lib/features/auth/presentation/bloc/login_event.dart
Normal file
9
lib/features/auth/presentation/bloc/login_event.dart
Normal file
@@ -0,0 +1,9 @@
|
||||
abstract class LoginEvent {}
|
||||
|
||||
class LoginSubmitted extends LoginEvent {
|
||||
final String username;
|
||||
final String password;
|
||||
final int sourceType;
|
||||
|
||||
LoginSubmitted(this.username, this.password, this.sourceType);
|
||||
}
|
||||
17
lib/features/auth/presentation/bloc/login_state.dart
Normal file
17
lib/features/auth/presentation/bloc/login_state.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
import '../../../../core/domain/entities/user_entity.dart';
|
||||
|
||||
abstract class LoginState {}
|
||||
|
||||
class LoginInitial extends LoginState {}
|
||||
|
||||
class LoginLoading extends LoginState {}
|
||||
|
||||
class LoginSuccess extends LoginState {
|
||||
final UserEntity user;
|
||||
LoginSuccess(this.user);
|
||||
}
|
||||
|
||||
class LoginFailure extends LoginState {
|
||||
final String message;
|
||||
LoginFailure(this.message);
|
||||
}
|
||||
52
lib/features/auth/presentation/pages/login_page.dart
Normal file
52
lib/features/auth/presentation/pages/login_page.dart
Normal file
@@ -0,0 +1,52 @@
|
||||
import 'package:cc_ui_kit/cc_ui_kit.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../../../core/router/route_paths.dart';
|
||||
import '../bloc/login_cubit.dart';
|
||||
import '../bloc/login_state.dart';
|
||||
|
||||
class LoginPage extends StatelessWidget {
|
||||
final _userCtrl = TextEditingController();
|
||||
final _pwdCtrl = TextEditingController();
|
||||
|
||||
LoginPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Login')),
|
||||
body: BlocListener<LoginCubit, LoginState>(
|
||||
listener: (context, state) {
|
||||
if (state is LoginSuccess) {
|
||||
// Navigator.pushReplacement(
|
||||
// context,
|
||||
// MaterialPageRoute(builder: (_) => HomePage(user: state.user)),
|
||||
// );
|
||||
context.go(RoutePaths.home);
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
TextField(controller: _userCtrl),
|
||||
TextField(controller: _pwdCtrl, obscureText: true),
|
||||
CCPrimaryButton(
|
||||
onPressed: () {
|
||||
// context.read<LoginBloc>().add(
|
||||
// LoginSubmitted(_userCtrl.text, _pwdCtrl.text),
|
||||
// );
|
||||
context.read<LoginCubit>().login(
|
||||
_userCtrl.text,
|
||||
_pwdCtrl.text,
|
||||
2,
|
||||
);
|
||||
},
|
||||
text: 'Login',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user