feat: improve ui
This commit is contained in:
parent
940f73809c
commit
44f5703de4
4 changed files with 513 additions and 253 deletions
|
|
@ -16,18 +16,30 @@ class _LoginScreenState extends ConsumerState<LoginScreen> {
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
final _emailController = TextEditingController();
|
final _emailController = TextEditingController();
|
||||||
final _passwordController = TextEditingController();
|
final _passwordController = TextEditingController();
|
||||||
|
final _emailFocusNode = FocusNode();
|
||||||
|
final _passwordFocusNode = FocusNode();
|
||||||
|
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
bool _obscurePassword = true;
|
bool _obscurePassword = true;
|
||||||
|
String? _errorMessage;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_emailController.dispose();
|
_emailController.dispose();
|
||||||
_passwordController.dispose();
|
_passwordController.dispose();
|
||||||
|
_emailFocusNode.dispose();
|
||||||
|
_passwordFocusNode.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _handleLogin() async {
|
Future<void> _handleLogin() async {
|
||||||
if (!_formKey.currentState!.validate()) return;
|
FocusScope.of(context).unfocus();
|
||||||
|
|
||||||
|
setState(() => _errorMessage = null);
|
||||||
|
|
||||||
|
if (!_formKey.currentState!.validate()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setState(() => _isLoading = true);
|
setState(() => _isLoading = true);
|
||||||
|
|
||||||
|
|
@ -43,147 +55,235 @@ class _LoginScreenState extends ConsumerState<LoginScreen> {
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
String message = 'Login failed. Please try again.';
|
setState(() {
|
||||||
final errorStr = e.toString();
|
_isLoading = false;
|
||||||
|
_errorMessage = _parseErrorMessage(e.toString());
|
||||||
if (errorStr.contains('400')) {
|
});
|
||||||
message = 'Invalid email or password.';
|
|
||||||
} else if (errorStr.contains('SocketException') ||
|
|
||||||
errorStr.contains('Connection refused') ||
|
|
||||||
errorStr.contains('Network is unreachable')) {
|
|
||||||
message = 'Could not connect to server. Please check your internet.';
|
|
||||||
}
|
|
||||||
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text(message),
|
|
||||||
backgroundColor: AppTheme.errorColor,
|
|
||||||
behavior: SnackBarBehavior.floating,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (mounted) {
|
|
||||||
setState(() => _isLoading = false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _parseErrorMessage(String error) {
|
||||||
|
if (error.contains('400')) {
|
||||||
|
return 'Invalid email or password';
|
||||||
|
} else if (error.contains('SocketException') ||
|
||||||
|
error.contains('Connection refused') ||
|
||||||
|
error.contains('Network is unreachable')) {
|
||||||
|
return 'Could not connect to server.\nPlease check your internet connection.';
|
||||||
|
} else if (error.contains('timeout')) {
|
||||||
|
return 'Connection timeout.\nPlease try again.';
|
||||||
|
}
|
||||||
|
return 'Login failed. Please try again.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: SafeArea(
|
body: GestureDetector(
|
||||||
child: Center(
|
onTap: () => FocusScope.of(context).unfocus(),
|
||||||
child: SingleChildScrollView(
|
child: SafeArea(
|
||||||
padding: const EdgeInsets.all(24),
|
child: LayoutBuilder(
|
||||||
child: Form(
|
builder: (context, constraints) {
|
||||||
key: _formKey,
|
return SingleChildScrollView(
|
||||||
child: Column(
|
physics: const ClampingScrollPhysics(),
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
child: ConstrainedBox(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
constraints: BoxConstraints(
|
||||||
children: [
|
minHeight: constraints.maxHeight,
|
||||||
Container(
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: AppTheme.primaryColor,
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
Icons.fitness_center,
|
|
||||||
size: 56,
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
child: IntrinsicHeight(
|
||||||
Text(
|
child: Padding(
|
||||||
'WELCOME BACK',
|
padding: const EdgeInsets.all(24),
|
||||||
style: Theme.of(context).textTheme.displayMedium,
|
child: Form(
|
||||||
textAlign: TextAlign.center,
|
key: _formKey,
|
||||||
),
|
child: Column(
|
||||||
const SizedBox(height: 8),
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
Text(
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
'Time to level up your strength',
|
children: [
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
const Spacer(),
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
Container(
|
||||||
const SizedBox(height: 48),
|
width: 100,
|
||||||
TextFormField(
|
height: 100,
|
||||||
controller: _emailController,
|
decoration: BoxDecoration(
|
||||||
keyboardType: TextInputType.emailAddress,
|
color: AppTheme.primaryColor,
|
||||||
decoration: const InputDecoration(
|
borderRadius: BorderRadius.circular(20),
|
||||||
labelText: 'Email',
|
boxShadow: [
|
||||||
prefixIcon: Icon(Icons.email_outlined),
|
BoxShadow(
|
||||||
),
|
color: AppTheme.primaryColor
|
||||||
validator: (value) {
|
.withValues(alpha: 0.4),
|
||||||
if (value == null || value.isEmpty) {
|
blurRadius: 20,
|
||||||
return 'Please enter your email';
|
spreadRadius: 2,
|
||||||
}
|
),
|
||||||
if (!value.contains('@')) {
|
],
|
||||||
return 'Please enter a valid email';
|
),
|
||||||
}
|
child: const Icon(
|
||||||
return null;
|
Icons.fitness_center,
|
||||||
},
|
size: 56,
|
||||||
),
|
color: Colors.black,
|
||||||
const SizedBox(height: 16),
|
),
|
||||||
TextFormField(
|
|
||||||
controller: _passwordController,
|
|
||||||
obscureText: _obscurePassword,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: 'Password',
|
|
||||||
prefixIcon: const Icon(Icons.lock_outline),
|
|
||||||
suffixIcon: IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
_obscurePassword
|
|
||||||
? Icons.visibility_outlined
|
|
||||||
: Icons.visibility_off_outlined,
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
setState(() => _obscurePassword = !_obscurePassword);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
validator: (value) {
|
|
||||||
if (value == null || value.isEmpty) {
|
|
||||||
return 'Please enter your password';
|
|
||||||
}
|
|
||||||
if (value.length < 8) {
|
|
||||||
return 'Password must be at least 8 characters';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: _isLoading ? null : _handleLogin,
|
|
||||||
child: _isLoading
|
|
||||||
? const SizedBox(
|
|
||||||
height: 20,
|
|
||||||
width: 20,
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
strokeWidth: 2,
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
),
|
||||||
)
|
const SizedBox(height: 32),
|
||||||
: const Text('LOGIN'),
|
|
||||||
),
|
Text(
|
||||||
const SizedBox(height: 16),
|
'WELCOME BACK',
|
||||||
Row(
|
style: Theme.of(context).textTheme.displayMedium,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
textAlign: TextAlign.center,
|
||||||
children: [
|
),
|
||||||
Text(
|
const SizedBox(height: 8),
|
||||||
"Don't have an account? ",
|
Text(
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
'Time to level up your strength',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 48),
|
||||||
|
|
||||||
|
if (_errorMessage != null)
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
margin: const EdgeInsets.only(bottom: 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppTheme.errorColor
|
||||||
|
.withValues(alpha: 0.1),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(
|
||||||
|
color: AppTheme.errorColor,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.error_outline,
|
||||||
|
color: AppTheme.errorColor,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
_errorMessage!,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: AppTheme.errorColor,
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
TextFormField(
|
||||||
|
controller: _emailController,
|
||||||
|
focusNode: _emailFocusNode,
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
textInputAction: TextInputAction.next,
|
||||||
|
enabled: !_isLoading,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Email',
|
||||||
|
prefixIcon: Icon(Icons.email_outlined),
|
||||||
|
),
|
||||||
|
onFieldSubmitted: (_) {
|
||||||
|
_passwordFocusNode.requestFocus();
|
||||||
|
},
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return 'Please enter your email';
|
||||||
|
}
|
||||||
|
if (!value.contains('@')) {
|
||||||
|
return 'Please enter a valid email';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
// Password Field
|
||||||
|
TextFormField(
|
||||||
|
controller: _passwordController,
|
||||||
|
focusNode: _passwordFocusNode,
|
||||||
|
obscureText: _obscurePassword,
|
||||||
|
textInputAction: TextInputAction.done,
|
||||||
|
enabled: !_isLoading,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Password',
|
||||||
|
prefixIcon: const Icon(Icons.lock_outline),
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
_obscurePassword
|
||||||
|
? Icons.visibility_outlined
|
||||||
|
: Icons.visibility_off_outlined,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_obscurePassword = !_obscurePassword;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onFieldSubmitted: (_) => _handleLogin(),
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return 'Please enter your password';
|
||||||
|
}
|
||||||
|
if (value.length < 8) {
|
||||||
|
return 'Password must be at least 8 characters';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _isLoading ? null : _handleLogin,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(vertical: 16),
|
||||||
|
disabledBackgroundColor: AppTheme.primaryColor
|
||||||
|
.withValues(alpha: 0.5),
|
||||||
|
),
|
||||||
|
child: _isLoading
|
||||||
|
? const SizedBox(
|
||||||
|
height: 20,
|
||||||
|
width: 20,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const Text(
|
||||||
|
'LOGIN',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
letterSpacing: 1.2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Don't have an account? ",
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: _isLoading
|
||||||
|
? null
|
||||||
|
: () => context.go('/register'),
|
||||||
|
child: const Text('REGISTER'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
const Spacer(),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
TextButton(
|
),
|
||||||
onPressed: () => context.go('/register'),
|
|
||||||
child: const Text('REGISTER'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
);
|
||||||
),
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,180 @@
|
||||||
|
// import 'package:flutter/material.dart';
|
||||||
|
// import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
// import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
// import '../../../../core/theme/app_theme.dart';
|
||||||
|
// import '../../../../core/constants/app_constants.dart';
|
||||||
|
// import '../../../onboarding/presentation/screens/bodyweight_input_screen.dart';
|
||||||
|
|
||||||
|
// class RegisterScreen extends ConsumerStatefulWidget {
|
||||||
|
// const RegisterScreen({super.key});
|
||||||
|
|
||||||
|
// @override
|
||||||
|
// ConsumerState<RegisterScreen> createState() => _RegisterScreenState();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// class _RegisterScreenState extends ConsumerState<RegisterScreen> {
|
||||||
|
// final _formKey = GlobalKey<FormState>();
|
||||||
|
// final _emailController = TextEditingController();
|
||||||
|
// // final _passwordController = TextEditingController();
|
||||||
|
// // final _confirmPasswordController = TextEditingController();
|
||||||
|
// // bool _obscurePassword = true;
|
||||||
|
// // bool _obscureConfirmPassword = true;
|
||||||
|
|
||||||
|
// @override
|
||||||
|
// void dispose() {
|
||||||
|
// _emailController.dispose();
|
||||||
|
// // _passwordController.dispose();
|
||||||
|
// // _confirmPasswordController.dispose();
|
||||||
|
// super.dispose();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void _handleRegister() {
|
||||||
|
// if (!_formKey.currentState!.validate()) return;
|
||||||
|
|
||||||
|
// ref.read(onboardingDataProvider.notifier).updateData({
|
||||||
|
// 'email': _emailController.text.trim(),
|
||||||
|
// // 'password': _passwordController.text,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// context.go('/onboarding/welcome');
|
||||||
|
// }
|
||||||
|
|
||||||
|
// @override
|
||||||
|
// Widget build(BuildContext context) {
|
||||||
|
// return Scaffold(
|
||||||
|
// appBar: AppBar(
|
||||||
|
// leading: IconButton(
|
||||||
|
// icon: const Icon(Icons.arrow_back),
|
||||||
|
// onPressed: () => context.go('/login'),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// body: SafeArea(
|
||||||
|
// child: Center(
|
||||||
|
// child: SingleChildScrollView(
|
||||||
|
// padding: const EdgeInsets.all(24),
|
||||||
|
// child: Form(
|
||||||
|
// key: _formKey,
|
||||||
|
// child: Column(
|
||||||
|
// crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
// children: [
|
||||||
|
// Text(
|
||||||
|
// 'CREATE ACCOUNT',
|
||||||
|
// style: Theme.of(context).textTheme.displayMedium,
|
||||||
|
// textAlign: TextAlign.center,
|
||||||
|
// ),
|
||||||
|
// const SizedBox(height: 8),
|
||||||
|
// Text(
|
||||||
|
// 'Begin your strength journey',
|
||||||
|
// style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
// textAlign: TextAlign.center,
|
||||||
|
// ),
|
||||||
|
// const SizedBox(height: 48),
|
||||||
|
// TextFormField(
|
||||||
|
// controller: _emailController,
|
||||||
|
// keyboardType: TextInputType.emailAddress,
|
||||||
|
// decoration: const InputDecoration(
|
||||||
|
// labelText: 'Email',
|
||||||
|
// prefixIcon: Icon(Icons.email_outlined),
|
||||||
|
// ),
|
||||||
|
// validator: (value) {
|
||||||
|
// if (value == null || value.isEmpty) {
|
||||||
|
// return 'Please enter your email';
|
||||||
|
// }
|
||||||
|
// if (!value.contains('@')) {
|
||||||
|
// return 'Please enter a valid email';
|
||||||
|
// }
|
||||||
|
// return null;
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// // const SizedBox(height: 16),
|
||||||
|
// // TextFormField(
|
||||||
|
// // controller: _passwordController,
|
||||||
|
// // obscureText: _obscurePassword,
|
||||||
|
// // decoration: InputDecoration(
|
||||||
|
// // labelText: 'Password',
|
||||||
|
// // prefixIcon: const Icon(Icons.lock_outline),
|
||||||
|
// // suffixIcon: IconButton(
|
||||||
|
// // icon: Icon(
|
||||||
|
// // _obscurePassword
|
||||||
|
// // ? Icons.visibility_outlined
|
||||||
|
// // : Icons.visibility_off_outlined,
|
||||||
|
// // ),
|
||||||
|
// // onPressed: () {
|
||||||
|
// // setState(() => _obscurePassword = !_obscurePassword);
|
||||||
|
// // },
|
||||||
|
// // ),
|
||||||
|
// // ),
|
||||||
|
// // validator: (value) {
|
||||||
|
// // if (value == null || value.isEmpty) {
|
||||||
|
// // return 'Please enter a password';
|
||||||
|
// // }
|
||||||
|
// // if (value.length < 8) {
|
||||||
|
// // return 'Password must be at least 8 characters';
|
||||||
|
// // }
|
||||||
|
// // return null;
|
||||||
|
// // },
|
||||||
|
// // ),
|
||||||
|
// // const SizedBox(height: 16),
|
||||||
|
// // TextFormField(
|
||||||
|
// // controller: _confirmPasswordController,
|
||||||
|
// // obscureText: _obscureConfirmPassword,
|
||||||
|
// // decoration: InputDecoration(
|
||||||
|
// // labelText: 'Confirm Password',
|
||||||
|
// // prefixIcon: const Icon(Icons.lock_outline),
|
||||||
|
// // suffixIcon: IconButton(
|
||||||
|
// // icon: Icon(
|
||||||
|
// // _obscureConfirmPassword
|
||||||
|
// // ? Icons.visibility_outlined
|
||||||
|
// // : Icons.visibility_off_outlined,
|
||||||
|
// // ),
|
||||||
|
// // onPressed: () {
|
||||||
|
// // setState(() => _obscureConfirmPassword =
|
||||||
|
// // !_obscureConfirmPassword);
|
||||||
|
// // },
|
||||||
|
// // ),
|
||||||
|
// // ),
|
||||||
|
// // validator: (value) {
|
||||||
|
// // if (value != _passwordController.text) {
|
||||||
|
// // return 'Passwords do not match';
|
||||||
|
// // }
|
||||||
|
// // return null;
|
||||||
|
// // },
|
||||||
|
// // ),
|
||||||
|
// const SizedBox(height: 32),
|
||||||
|
// ElevatedButton(
|
||||||
|
// onPressed: _handleRegister,
|
||||||
|
// child: const Text('CONTINUE'),
|
||||||
|
// ),
|
||||||
|
// const SizedBox(height: 16),
|
||||||
|
// Row(
|
||||||
|
// mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
// children: [
|
||||||
|
// Text(
|
||||||
|
// 'Already have an account? ',
|
||||||
|
// style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
// ),
|
||||||
|
// TextButton(
|
||||||
|
// onPressed: () => context.go('/login'),
|
||||||
|
// child: const Text('LOGIN'),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
import '../../../../core/theme/app_theme.dart';
|
import '../../../../core/theme/app_theme.dart';
|
||||||
import '../../../../core/constants/app_constants.dart';
|
|
||||||
import '../../../onboarding/presentation/screens/bodyweight_input_screen.dart';
|
import '../../../onboarding/presentation/screens/bodyweight_input_screen.dart';
|
||||||
|
|
||||||
class RegisterScreen extends ConsumerStatefulWidget {
|
class RegisterScreen extends ConsumerStatefulWidget {
|
||||||
|
|
@ -16,25 +187,24 @@ class RegisterScreen extends ConsumerStatefulWidget {
|
||||||
class _RegisterScreenState extends ConsumerState<RegisterScreen> {
|
class _RegisterScreenState extends ConsumerState<RegisterScreen> {
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
final _emailController = TextEditingController();
|
final _emailController = TextEditingController();
|
||||||
// final _passwordController = TextEditingController();
|
final _emailFocusNode = FocusNode();
|
||||||
// final _confirmPasswordController = TextEditingController();
|
|
||||||
// bool _obscurePassword = true;
|
|
||||||
// bool _obscureConfirmPassword = true;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_emailController.dispose();
|
_emailController.dispose();
|
||||||
// _passwordController.dispose();
|
_emailFocusNode.dispose();
|
||||||
// _confirmPasswordController.dispose();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleRegister() {
|
void _handleRegister() {
|
||||||
if (!_formKey.currentState!.validate()) return;
|
FocusScope.of(context).unfocus();
|
||||||
|
|
||||||
|
if (!_formKey.currentState!.validate()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ref.read(onboardingDataProvider.notifier).updateData({
|
ref.read(onboardingDataProvider.notifier).updateData({
|
||||||
'email': _emailController.text.trim(),
|
'email': _emailController.text.trim(),
|
||||||
// 'password': _passwordController.text,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
context.go('/onboarding/welcome');
|
context.go('/onboarding/welcome');
|
||||||
|
|
@ -49,120 +219,98 @@ class _RegisterScreenState extends ConsumerState<RegisterScreen> {
|
||||||
onPressed: () => context.go('/login'),
|
onPressed: () => context.go('/login'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: GestureDetector(
|
||||||
child: Center(
|
onTap: () => FocusScope.of(context).unfocus(),
|
||||||
child: SingleChildScrollView(
|
child: SafeArea(
|
||||||
padding: const EdgeInsets.all(24),
|
child: LayoutBuilder(
|
||||||
child: Form(
|
builder: (context, constraints) {
|
||||||
key: _formKey,
|
return SingleChildScrollView(
|
||||||
child: Column(
|
physics: const ClampingScrollPhysics(),
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
child: ConstrainedBox(
|
||||||
children: [
|
constraints: BoxConstraints(
|
||||||
Text(
|
minHeight: constraints.maxHeight,
|
||||||
'CREATE ACCOUNT',
|
|
||||||
style: Theme.of(context).textTheme.displayMedium,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
child: IntrinsicHeight(
|
||||||
Text(
|
child: Padding(
|
||||||
'Begin your strength journey',
|
padding: const EdgeInsets.all(24),
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
child: Form(
|
||||||
textAlign: TextAlign.center,
|
key: _formKey,
|
||||||
),
|
child: Column(
|
||||||
const SizedBox(height: 48),
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
TextFormField(
|
children: [
|
||||||
controller: _emailController,
|
const Spacer(),
|
||||||
keyboardType: TextInputType.emailAddress,
|
Text(
|
||||||
decoration: const InputDecoration(
|
'CREATE ACCOUNT',
|
||||||
labelText: 'Email',
|
style: Theme.of(context).textTheme.displayMedium,
|
||||||
prefixIcon: Icon(Icons.email_outlined),
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'Begin your strength journey',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 48),
|
||||||
|
TextFormField(
|
||||||
|
controller: _emailController,
|
||||||
|
focusNode: _emailFocusNode,
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
textInputAction: TextInputAction.next,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Email',
|
||||||
|
prefixIcon: Icon(Icons.email_outlined),
|
||||||
|
helperText: 'You will use this to login',
|
||||||
|
),
|
||||||
|
onFieldSubmitted: (_) {},
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return 'Please enter your email';
|
||||||
|
}
|
||||||
|
if (!value.contains('@')) {
|
||||||
|
return 'Please enter a valid email';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _handleRegister,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(vertical: 16),
|
||||||
|
),
|
||||||
|
child: const Text(
|
||||||
|
'CONTINUE',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
letterSpacing: 1.2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Already have an account? ',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => context.go('/login'),
|
||||||
|
child: const Text('LOGIN'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
validator: (value) {
|
|
||||||
if (value == null || value.isEmpty) {
|
|
||||||
return 'Please enter your email';
|
|
||||||
}
|
|
||||||
if (!value.contains('@')) {
|
|
||||||
return 'Please enter a valid email';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
// const SizedBox(height: 16),
|
),
|
||||||
// TextFormField(
|
);
|
||||||
// controller: _passwordController,
|
},
|
||||||
// obscureText: _obscurePassword,
|
|
||||||
// decoration: InputDecoration(
|
|
||||||
// labelText: 'Password',
|
|
||||||
// prefixIcon: const Icon(Icons.lock_outline),
|
|
||||||
// suffixIcon: IconButton(
|
|
||||||
// icon: Icon(
|
|
||||||
// _obscurePassword
|
|
||||||
// ? Icons.visibility_outlined
|
|
||||||
// : Icons.visibility_off_outlined,
|
|
||||||
// ),
|
|
||||||
// onPressed: () {
|
|
||||||
// setState(() => _obscurePassword = !_obscurePassword);
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// validator: (value) {
|
|
||||||
// if (value == null || value.isEmpty) {
|
|
||||||
// return 'Please enter a password';
|
|
||||||
// }
|
|
||||||
// if (value.length < 8) {
|
|
||||||
// return 'Password must be at least 8 characters';
|
|
||||||
// }
|
|
||||||
// return null;
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// const SizedBox(height: 16),
|
|
||||||
// TextFormField(
|
|
||||||
// controller: _confirmPasswordController,
|
|
||||||
// obscureText: _obscureConfirmPassword,
|
|
||||||
// decoration: InputDecoration(
|
|
||||||
// labelText: 'Confirm Password',
|
|
||||||
// prefixIcon: const Icon(Icons.lock_outline),
|
|
||||||
// suffixIcon: IconButton(
|
|
||||||
// icon: Icon(
|
|
||||||
// _obscureConfirmPassword
|
|
||||||
// ? Icons.visibility_outlined
|
|
||||||
// : Icons.visibility_off_outlined,
|
|
||||||
// ),
|
|
||||||
// onPressed: () {
|
|
||||||
// setState(() => _obscureConfirmPassword =
|
|
||||||
// !_obscureConfirmPassword);
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// validator: (value) {
|
|
||||||
// if (value != _passwordController.text) {
|
|
||||||
// return 'Passwords do not match';
|
|
||||||
// }
|
|
||||||
// return null;
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: _handleRegister,
|
|
||||||
child: const Text('CONTINUE'),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Already have an account? ',
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => context.go('/login'),
|
|
||||||
child: const Text('LOGIN'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -382,6 +382,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.4.1"
|
version: "3.4.1"
|
||||||
|
flutter_dotenv:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_dotenv
|
||||||
|
sha256: b7c7be5cd9f6ef7a78429cabd2774d3c4af50e79cb2b7593e3d5d763ef95c61b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.2.1"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
name: slrpg_app
|
name: slrpg_app
|
||||||
description: Streetlifting RPG - Gamified Training App
|
description: Streetlifting RPG - Gamified Training App
|
||||||
publish_to: 'none'
|
publish_to: "none"
|
||||||
version: 1.0.0+1
|
version: 1.0.0+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.2.0 <4.0.0'
|
sdk: ">=3.2.0 <4.0.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
audioplayers: ^6.0.0
|
audioplayers: ^6.0.0
|
||||||
|
flutter_dotenv: ^5.1.0
|
||||||
|
|
||||||
# State Management
|
# State Management
|
||||||
flutter_riverpod: ^3.1.0
|
flutter_riverpod: ^3.1.0
|
||||||
|
|
@ -69,6 +70,9 @@ flutter:
|
||||||
- assets/images/enemies/
|
- assets/images/enemies/
|
||||||
- assets/images/backgrounds/
|
- assets/images/backgrounds/
|
||||||
- assets/audio/
|
- assets/audio/
|
||||||
|
- .env
|
||||||
|
- .env.development
|
||||||
|
- .env.production
|
||||||
|
|
||||||
# fonts:
|
# fonts:
|
||||||
# - family: PixelFont
|
# - family: PixelFont
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue