import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:reactive_forms/reactive_forms.dart'; import '../../../../core/router/route_names.dart'; import '../../../../core/utils/extensions.dart'; import '../../../../shared/providers/auth_provider.dart'; import '../providers/auth_providers.dart'; class RegisterForm extends ConsumerStatefulWidget { const RegisterForm({super.key}); @override ConsumerState createState() => _RegisterFormState(); } class _RegisterFormState extends ConsumerState { late final FormGroup form; @override void initState() { super.initState(); form = FormGroup({ 'name': FormControl( validators: [Validators.required], ), 'email': FormControl( validators: [Validators.required, Validators.email], ), 'password': FormControl( validators: [Validators.required, Validators.minLength(8)], ), 'confirmPassword': FormControl( validators: [Validators.required], ), }, validators: [ Validators.mustMatch('password', 'confirmPassword'), ]); } @override void dispose() { form.dispose(); super.dispose(); } Future _onSubmit() async { if (!form.valid) { form.markAllAsTouched(); return; } final name = form.control('name').value as String; final email = form.control('email').value as String; final password = form.control('password').value as String; final user = await ref.read(registerNotifierProvider.notifier).register( email: email, password: password, name: name, ); if (user != null && mounted) { ref.read(authStateProvider.notifier).setUser(user); } } @override Widget build(BuildContext context) { final registerState = ref.watch(registerNotifierProvider); ref.listen(registerNotifierProvider, (_, state) { if (state.hasError) { context.showSnackBar( '회원가입에 실패했습니다. 입력 정보를 확인해주세요.', isError: true, ); } }); return ReactiveForm( formGroup: form, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: [ Text( '회원가입', style: context.textTheme.headlineMedium?.copyWith( fontWeight: FontWeight.bold, ), textAlign: TextAlign.center, ), const Gap(8), Text( '새 계정을 만드세요', style: context.textTheme.bodyMedium?.copyWith( color: context.colorScheme.onSurfaceVariant, ), textAlign: TextAlign.center, ), const Gap(32), ReactiveTextField( formControlName: 'name', decoration: const InputDecoration( labelText: '이름', hintText: '홍길동', prefixIcon: Icon(Icons.person_outlined), ), validationMessages: { 'required': (_) => '이름을 입력해주세요', }, ), const Gap(16), ReactiveTextField( formControlName: 'email', decoration: const InputDecoration( labelText: '이메일', hintText: 'email@example.com', prefixIcon: Icon(Icons.email_outlined), ), keyboardType: TextInputType.emailAddress, validationMessages: { 'required': (_) => '이메일을 입력해주세요', 'email': (_) => '올바른 이메일 형식이 아닙니다', }, ), const Gap(16), ReactiveTextField( formControlName: 'password', decoration: const InputDecoration( labelText: '비밀번호', hintText: '8자 이상 입력', prefixIcon: Icon(Icons.lock_outlined), ), obscureText: true, validationMessages: { 'required': (_) => '비밀번호를 입력해주세요', 'minLength': (_) => '비밀번호는 8자 이상이어야 합니다', }, ), const Gap(16), ReactiveTextField( formControlName: 'confirmPassword', decoration: const InputDecoration( labelText: '비밀번호 확인', hintText: '비밀번호를 다시 입력', prefixIcon: Icon(Icons.lock_outlined), ), obscureText: true, validationMessages: { 'required': (_) => '비밀번호 확인을 입력해주세요', 'mustMatch': (_) => '비밀번호가 일치하지 않습니다', }, ), const Gap(24), FilledButton( onPressed: registerState.isLoading ? null : _onSubmit, child: registerState.isLoading ? const SizedBox( height: 20, width: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : const Text('회원가입'), ), const Gap(16), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('이미 계정이 있으신가요?'), TextButton( onPressed: () => context.goNamed(RouteNames.login), child: const Text('로그인'), ), ], ), ], ), ); } }