import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:formz/formz.dart'; import 'package:go_router/go_router.dart'; import '../../../../core/di/injection.dart'; import '../../../../core/theme/design_tokens.dart'; import '../../../../shared/widgets/app_button.dart'; import '../../../../shared/widgets/banner/app_banner.dart'; import '../../../../shared/widgets/link_button.dart'; import '../../../../shared/widgets/toast/toast_type.dart'; import '../../data/auth_repository.dart'; import '../../presentation/bloc/auth_bloc.dart'; import '../../presentation/bloc/auth_event.dart'; import '../../presentation/cubits/login_cubit.dart'; import '../widgets/auth_field.dart'; import '../widgets/auth_page_scaffold.dart'; import '../widgets/password_field.dart'; class LoginScreen extends StatelessWidget { const LoginScreen({super.key}); @override Widget build(BuildContext context) { return BlocProvider( create: (context) => LoginCubit(sl()), child: const LoginView(), ); } } class LoginView extends StatefulWidget { const LoginView({super.key}); @override State createState() => _LoginViewState(); } class _LoginViewState extends State { final _emailController = TextEditingController(); final _passwordController = TextEditingController(); @override void dispose() { _emailController.dispose(); _passwordController.dispose(); super.dispose(); } Future _handleLogin() async { final cubit = context.read(); cubit.emailChanged(_emailController.text); cubit.passwordChanged(_passwordController.text); if (!cubit.state.isValid) { return; } final response = await cubit.submit(); if (response != null && mounted) { context.read().add(AuthLoggedIn(user: response.user)); context.go('/home'); } } @override Widget build(BuildContext context) { return AuthPageScaffold( mainContentKey: const Key('login_main_content'), footerKey: const Key('login_footer'), mainContent: Padding( padding: const EdgeInsets.symmetric(horizontal: AppSpacing.sm), child: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 380), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ const AuthHeroHeader(showBrand: true), SizedBox(height: AppSpacing.xxl), BlocBuilder( builder: (context, state) { final fieldError = state.email.displayError != null ? state.email.error : state.password.displayError != null ? state.password.error : null; return AuthSurfaceCard( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Text( '登录账号', style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, color: AppColors.slate900, ), ), SizedBox(height: AppSpacing.xs), SizedBox(height: AppSpacing.xl), AuthSection( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ AuthField( label: '邮箱', hint: 'name@example.com', controller: _emailController, keyboardType: TextInputType.emailAddress, ), SizedBox(height: AppSpacing.lg), PasswordField( controller: _passwordController, label: '密码', hint: '请输入密码', ), if (state.errorMessage != null || fieldError != null) Padding( padding: const EdgeInsets.only( top: AppSpacing.lg, ), child: AppBanner( message: state.errorMessage ?? fieldError!, type: state.errorMessage != null ? ToastType.error : ToastType.warning, title: state.errorMessage != null ? '登录失败' : '请检查输入', ), ), ], ), ), SizedBox(height: AppSpacing.xl), AppButton( text: '登录', onPressed: state.status == FormzSubmissionStatus.inProgress ? null : _handleLogin, isLoading: state.status == FormzSubmissionStatus.inProgress, ), SizedBox(height: AppSpacing.sm), Align( alignment: Alignment.centerRight, child: LinkButton( text: '忘记密码?', onTap: () => context.push('/reset-password'), ), ), ], ), ); }, ), ], ), ), ), ), footer: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ const Text( '还没有账号?', style: TextStyle(fontSize: 14, color: AppColors.authLinkMuted), ), LinkButton(text: '去注册', onTap: () => context.push('/register')), ], ), ); } }