first commit
This commit is contained in:
126
lib/features/auth/presentation/pages/forgot_password_page.dart
Normal file
126
lib/features/auth/presentation/pages/forgot_password_page.dart
Normal file
@@ -0,0 +1,126 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../../../core/router/app_routes.dart';
|
||||
import '../../../../core/theme/app_colors.dart';
|
||||
import '../../../../core/widgets/riotz_scaffold.dart';
|
||||
import '../../../../core/widgets/riotz_button.dart';
|
||||
import '../providers/auth_providers.dart';
|
||||
|
||||
class ForgotPasswordPage extends ConsumerStatefulWidget {
|
||||
const ForgotPasswordPage({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<ForgotPasswordPage> createState() => _ForgotPasswordPageState();
|
||||
}
|
||||
|
||||
class _ForgotPasswordPageState extends ConsumerState<ForgotPasswordPage> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _emailController = TextEditingController();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_emailController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
ref.listen(authControllerProvider, (_, next) {
|
||||
next.whenOrNull(
|
||||
data: (_) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
backgroundColor: AppColors.success,
|
||||
content: Text(
|
||||
'RECOVERY SIGNAL SENT. CHECK YOUR INBOX.',
|
||||
style: TextStyle(color: AppColors.black, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
);
|
||||
if (mounted) context.go(AppRoutes.login);
|
||||
},
|
||||
error: (error, _) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: AppColors.bloodRed,
|
||||
content: Text(
|
||||
error.toString().toUpperCase(),
|
||||
style: const TextStyle(color: AppColors.white, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
final authState = ref.watch(authControllerProvider);
|
||||
final loading = authState.isLoading;
|
||||
|
||||
return RiotzScaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('RECOVER IDENTITY'),
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back_ios_new, size: 20),
|
||||
onPressed: () => context.go(AppRoutes.login),
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'LOST SIGNAL',
|
||||
style: theme.textTheme.displayMedium?.copyWith(height: 1),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'ENTER YOUR EMAIL TO RESTORE ACCESS.',
|
||||
style: theme.textTheme.labelLarge?.copyWith(color: AppColors.neonRed),
|
||||
),
|
||||
const SizedBox(height: 48),
|
||||
TextFormField(
|
||||
controller: _emailController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'EMAIL ADDRESS',
|
||||
prefixIcon: Icon(Icons.email_outlined, color: AppColors.grey),
|
||||
),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
validator: (v) => (v == null || !v.contains('@'))
|
||||
? 'INVALID EMAIL'
|
||||
: null,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
RiotzButton(
|
||||
label: 'SEND RECOVERY LINK',
|
||||
isLoading: loading,
|
||||
onPressed: () async {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
await ref
|
||||
.read(authControllerProvider.notifier)
|
||||
.forgotPassword(_emailController.text);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Center(
|
||||
child: TextButton(
|
||||
onPressed: loading ? null : () => context.go(AppRoutes.login),
|
||||
child: Text(
|
||||
'REMEMBERED? BACK TO LOGIN',
|
||||
style: theme.textTheme.labelLarge?.copyWith(color: AppColors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
142
lib/features/auth/presentation/pages/login_page.dart
Normal file
142
lib/features/auth/presentation/pages/login_page.dart
Normal file
@@ -0,0 +1,142 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../../../core/router/app_routes.dart';
|
||||
import '../../../../core/widgets/riotz_scaffold.dart';
|
||||
import '../../../../core/widgets/riotz_button.dart';
|
||||
import '../../../../core/theme/app_colors.dart';
|
||||
import '../providers/auth_providers.dart';
|
||||
|
||||
class LoginPage extends ConsumerStatefulWidget {
|
||||
const LoginPage({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<LoginPage> createState() => _LoginPageState();
|
||||
}
|
||||
|
||||
class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _emailController = TextEditingController();
|
||||
final _passwordController = TextEditingController();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_emailController.dispose();
|
||||
_passwordController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
ref.listen(authControllerProvider, (_, next) {
|
||||
next.whenOrNull(
|
||||
data: (_) {
|
||||
if (mounted) context.go(AppRoutes.home);
|
||||
},
|
||||
error: (error, _) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: AppColors.bloodRed,
|
||||
content: Text(
|
||||
error.toString().toUpperCase(),
|
||||
style: const TextStyle(color: AppColors.white, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
final authState = ref.watch(authControllerProvider);
|
||||
final loading = authState.isLoading;
|
||||
|
||||
return RiotzScaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('IDENTIFY YOURSELF'),
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back_ios_new, size: 20),
|
||||
onPressed: () => context.go(AppRoutes.splash),
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'LOGIN',
|
||||
style: theme.textTheme.displayMedium?.copyWith(height: 1),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'ACCESS THE UNDERGROUND.',
|
||||
style: theme.textTheme.labelLarge?.copyWith(color: AppColors.neonRed),
|
||||
),
|
||||
const SizedBox(height: 48),
|
||||
TextFormField(
|
||||
controller: _emailController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'EMAIL ADDRESS',
|
||||
prefixIcon: Icon(Icons.email_outlined, color: AppColors.grey),
|
||||
),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
validator: (v) => (v == null || !v.contains('@'))
|
||||
? 'INVALID EMAIL'
|
||||
: null,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
controller: _passwordController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'PASSWORD',
|
||||
prefixIcon: Icon(Icons.lock_outline, color: AppColors.grey),
|
||||
),
|
||||
obscureText: true,
|
||||
validator: (v) =>
|
||||
(v == null || v.length < 6) ? 'PASSWORD TOO SHORT' : null,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
RiotzButton(
|
||||
label: 'ACCESS GRANTED',
|
||||
isLoading: loading,
|
||||
onPressed: () async {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
await ref.read(authControllerProvider.notifier).login(
|
||||
email: _emailController.text,
|
||||
password: _passwordController.text,
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Center(
|
||||
child: TextButton(
|
||||
onPressed: loading
|
||||
? null
|
||||
: () => context.push(AppRoutes.forgotPassword),
|
||||
child: Text(
|
||||
'FORGOT CREDENTIALS?',
|
||||
style: theme.textTheme.labelLarge?.copyWith(color: AppColors.grey),
|
||||
),
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: TextButton(
|
||||
onPressed: loading ? null : () => context.go(AppRoutes.signup),
|
||||
child: Text(
|
||||
'NO IDENTITY? SIGN UP',
|
||||
style: theme.textTheme.labelLarge?.copyWith(color: AppColors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
153
lib/features/auth/presentation/pages/signup_page.dart
Normal file
153
lib/features/auth/presentation/pages/signup_page.dart
Normal file
@@ -0,0 +1,153 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../../../core/router/app_routes.dart';
|
||||
import '../../../../core/widgets/riotz_scaffold.dart';
|
||||
import '../../../../core/widgets/riotz_button.dart';
|
||||
import '../../../../core/theme/app_colors.dart';
|
||||
import '../providers/auth_providers.dart';
|
||||
|
||||
class SignupPage extends ConsumerStatefulWidget {
|
||||
const SignupPage({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<SignupPage> createState() => _SignupPageState();
|
||||
}
|
||||
|
||||
class _SignupPageState extends ConsumerState<SignupPage> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _usernameController = TextEditingController();
|
||||
final _emailController = TextEditingController();
|
||||
final _passwordController = TextEditingController();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_usernameController.dispose();
|
||||
_emailController.dispose();
|
||||
_passwordController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
ref.listen(authControllerProvider, (_, next) {
|
||||
next.whenOrNull(
|
||||
data: (_) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
backgroundColor: AppColors.success,
|
||||
content: Text(
|
||||
'IDENTITY CREATED. CHECK EMAIL FOR CONFIRMATION.',
|
||||
style: TextStyle(color: AppColors.black, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
);
|
||||
if (mounted) context.go(AppRoutes.login);
|
||||
},
|
||||
error: (error, _) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: AppColors.bloodRed,
|
||||
content: Text(
|
||||
error.toString().toUpperCase(),
|
||||
style: const TextStyle(color: AppColors.white, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
final authState = ref.watch(authControllerProvider);
|
||||
final loading = authState.isLoading;
|
||||
|
||||
return RiotzScaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('CREATE IDENTITY'),
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back_ios_new, size: 20),
|
||||
onPressed: () => context.go(AppRoutes.login),
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'SIGN UP',
|
||||
style: theme.textTheme.displayMedium?.copyWith(height: 1),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'JOIN THE UNDERGROUND MOVEMENT.',
|
||||
style: theme.textTheme.labelLarge?.copyWith(color: AppColors.neonRed),
|
||||
),
|
||||
const SizedBox(height: 48),
|
||||
TextFormField(
|
||||
controller: _usernameController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'USERNAME',
|
||||
prefixIcon: Icon(Icons.person_outline, color: AppColors.grey),
|
||||
),
|
||||
validator: (v) =>
|
||||
(v == null || v.trim().length < 3) ? 'USERNAME TOO SHORT' : null,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
controller: _emailController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'EMAIL ADDRESS',
|
||||
prefixIcon: Icon(Icons.email_outlined, color: AppColors.grey),
|
||||
),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
validator: (v) => (v == null || !v.contains('@'))
|
||||
? 'INVALID EMAIL'
|
||||
: null,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
controller: _passwordController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'PASSWORD',
|
||||
prefixIcon: Icon(Icons.lock_outline, color: AppColors.grey),
|
||||
),
|
||||
obscureText: true,
|
||||
validator: (v) =>
|
||||
(v == null || v.length < 6) ? 'PASSWORD TOO SHORT' : null,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
RiotzButton(
|
||||
label: 'INITIALIZE RIOT',
|
||||
isLoading: loading,
|
||||
onPressed: () async {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
await ref.read(authControllerProvider.notifier).signup(
|
||||
email: _emailController.text,
|
||||
password: _passwordController.text,
|
||||
username: _usernameController.text,
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Center(
|
||||
child: TextButton(
|
||||
onPressed: loading ? null : () => context.go(AppRoutes.login),
|
||||
child: Text(
|
||||
'ALREADY HAVE AN IDENTITY? LOGIN',
|
||||
style: theme.textTheme.labelLarge?.copyWith(color: AppColors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user