first commit
This commit is contained in:
@@ -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_provider.dart';
|
||||
|
||||
class ForgotPasswordScreen extends ConsumerStatefulWidget {
|
||||
const ForgotPasswordScreen({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<ForgotPasswordScreen> createState() => _ForgotPasswordScreenState();
|
||||
}
|
||||
|
||||
class _ForgotPasswordScreenState extends ConsumerState<ForgotPasswordScreen> {
|
||||
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),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
149
lib/features/auth/presentation/screens/login_screen.dart
Normal file
149
lib/features/auth/presentation/screens/login_screen.dart
Normal file
@@ -0,0 +1,149 @@
|
||||
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_button.dart';
|
||||
import '../../../../core/widgets/riotz_scaffold.dart';
|
||||
import '../providers/auth_provider.dart';
|
||||
|
||||
class LoginScreen extends ConsumerStatefulWidget {
|
||||
const LoginScreen({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<LoginScreen> createState() => _LoginScreenState();
|
||||
}
|
||||
|
||||
class _LoginScreenState extends ConsumerState<LoginScreen> {
|
||||
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(
|
||||
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'),
|
||||
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(
|
||||
'ENTER THE VOID.',
|
||||
style: theme.textTheme.labelLarge?.copyWith(
|
||||
color: AppColors.neonRed,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 48),
|
||||
TextFormField(
|
||||
controller: _emailController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'EMAIL',
|
||||
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/screens/signup_screen.dart
Normal file
153
lib/features/auth/presentation/screens/signup_screen.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/theme/app_colors.dart';
|
||||
import '../../../../core/widgets/riotz_button.dart';
|
||||
import '../../../../core/widgets/riotz_scaffold.dart';
|
||||
import '../providers/auth_provider.dart';
|
||||
|
||||
class SignupScreen extends ConsumerStatefulWidget {
|
||||
const SignupScreen({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<SignupScreen> createState() => _SignupScreenState();
|
||||
}
|
||||
|
||||
class _SignupScreenState extends ConsumerState<SignupScreen> {
|
||||
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 INITIALIZED. 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 RIOT.',
|
||||
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',
|
||||
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