feat(apps): integrate auth cubits with register and login screens

This commit is contained in:
qzl
2026-02-25 15:21:26 +08:00
parent 0a7e1cd2d4
commit 8c1dfa9987
4 changed files with 301 additions and 126 deletions
@@ -1,26 +1,62 @@
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/theme/design_tokens.dart';
import '../../../../shared/widgets/app_button.dart';
import '../../../../shared/widgets/warning_banner.dart';
import '../../presentation/cubits/register_cubit.dart';
import '../../data/auth_repository.dart';
class RegisterScreen extends StatefulWidget {
class RegisterScreen extends StatelessWidget {
const RegisterScreen({super.key});
@override
State<RegisterScreen> createState() => _RegisterScreenState();
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => RegisterCubit(context.read<AuthRepository>()),
child: const RegisterView(),
);
}
}
class _RegisterScreenState extends State<RegisterScreen> {
class RegisterView extends StatefulWidget {
const RegisterView({super.key});
@override
State<RegisterView> createState() => _RegisterViewState();
}
class _RegisterViewState extends State<RegisterView> {
final _nicknameController = TextEditingController();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
bool _obscureText = true;
@override
void dispose() {
_nicknameController.dispose();
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}
Future<void> _handleNext() async {
final cubit = context.read<RegisterCubit>();
cubit.usernameChanged(_nicknameController.text);
cubit.emailChanged(_emailController.text);
cubit.passwordChanged(_passwordController.text);
if (!cubit.state.isStep1Valid) {
return;
}
final success = await cubit.submitStep1();
if (success && mounted) {
context.push('/register/step2', extra: cubit);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -93,23 +129,39 @@ class _RegisterScreenState extends State<RegisterScreen> {
}
Widget _buildFormContainer() {
return SizedBox(
width: 327,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildInput('昵称', '请输入昵称', _nicknameController),
const SizedBox(height: 12),
_buildInput('邮箱', '请输入邮箱', _emailController),
const SizedBox(height: 12),
_buildStepIndicator(),
const SizedBox(height: 12),
AppButton(
text: '下一步',
onPressed: () => context.push('/register/step2'),
return BlocBuilder<RegisterCubit, RegisterState>(
builder: (context, state) {
return SizedBox(
width: 327,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildInput('昵称', '请输入昵称', _nicknameController),
const SizedBox(height: 12),
_buildInput('邮箱', '请输入邮箱', _emailController),
const SizedBox(height: 12),
_buildPasswordInput(),
const SizedBox(height: 12),
_buildStepIndicator(),
if (state.errorMessage != null)
Padding(
padding: const EdgeInsets.only(top: 8),
child: WarningBanner(
message: state.errorMessage!,
visible: true,
),
),
const SizedBox(height: 12),
AppButton(
text: '下一步',
onPressed: state.status == FormzSubmissionStatus.inProgress
? null
: _handleNext,
),
],
),
],
),
);
},
);
}
@@ -138,6 +190,42 @@ class _RegisterScreenState extends State<RegisterScreen> {
);
}
Widget _buildPasswordInput() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'密码',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: Color(0xFF475569),
),
),
const SizedBox(height: 6),
TextField(
controller: _passwordController,
obscureText: _obscureText,
decoration: InputDecoration(
hintText: '请输入至少 6 位密码',
suffixIcon: IconButton(
icon: Icon(
_obscureText ? Icons.visibility_off : Icons.visibility,
size: 20,
color: AppColors.slate400,
),
onPressed: () {
setState(() {
_obscureText = !_obscureText;
});
},
),
),
),
],
);
}
Widget _buildStepIndicator() {
return Row(
children: [