baseproject
This commit is contained in:
277
base_project/lib/view/auth/admin_reg_view.dart
Normal file
277
base_project/lib/view/auth/admin_reg_view.dart
Normal file
@@ -0,0 +1,277 @@
|
||||
import 'package:base_project/commans/widgets/custom_textform_field.dart';
|
||||
import 'package:base_project/commans/widgets/custome_elevated_button.dart';
|
||||
import 'package:base_project/core/constants/ui_constants.dart';
|
||||
import 'package:base_project/core/providers/dynamic_theme_provider.dart';
|
||||
import 'package:base_project/utils/validator/text_feild_validator.dart';
|
||||
import 'package:base_project/view/auth/sign_up_view.dart';
|
||||
import 'package:base_project/view_model/auth/auth_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:iconsax/iconsax.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class AdminRegView extends StatefulWidget {
|
||||
AdminRegView({super.key});
|
||||
|
||||
@override
|
||||
State<AdminRegView> createState() => _AdminRegViewState();
|
||||
}
|
||||
|
||||
class _AdminRegViewState extends State<AdminRegView>
|
||||
with TickerProviderStateMixin {
|
||||
// Controllers and FormKey declarations
|
||||
final TextEditingController _companyNameController = TextEditingController();
|
||||
final TextEditingController _adminNameController = TextEditingController();
|
||||
final TextEditingController _emailController = TextEditingController();
|
||||
final TextEditingController _phoneController = TextEditingController();
|
||||
final TextEditingController _workspaceController = TextEditingController();
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
|
||||
late AnimationController _pageController;
|
||||
late AnimationController _cardController;
|
||||
late Animation<double> _fade;
|
||||
late Animation<Offset> _slide;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_pageController = AnimationController(
|
||||
vsync: this, duration: const Duration(milliseconds: 900));
|
||||
_cardController = AnimationController(
|
||||
vsync: this, duration: const Duration(milliseconds: 700));
|
||||
_fade = CurvedAnimation(parent: _pageController, curve: Curves.easeInOut);
|
||||
_slide = Tween<Offset>(begin: const Offset(0, 0.15), end: Offset.zero)
|
||||
.animate(CurvedAnimation(
|
||||
parent: _pageController, curve: Curves.easeOutCubic));
|
||||
_pageController.forward();
|
||||
Future.delayed(
|
||||
const Duration(milliseconds: 250), () => _cardController.forward());
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_companyNameController.dispose();
|
||||
_adminNameController.dispose();
|
||||
_emailController.dispose();
|
||||
_phoneController.dispose();
|
||||
_workspaceController.dispose();
|
||||
_pageController.dispose();
|
||||
_cardController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final textTheme = Theme.of(context).textTheme;
|
||||
|
||||
return Consumer<DynamicThemeProvider>(
|
||||
builder: (context, theme, child) {
|
||||
final colorScheme = theme.getCurrentColorScheme(
|
||||
Theme.of(context).brightness == Brightness.dark,
|
||||
);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
leading: IconButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
icon: const Icon(Icons.arrow_back_ios_new),
|
||||
color: Colors.white,
|
||||
),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
colorScheme.primary,
|
||||
colorScheme.primary.withOpacity(0.85),
|
||||
colorScheme.secondary.withOpacity(0.65),
|
||||
],
|
||||
stops: const [0.0, 0.6, 1.0],
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: FadeTransition(
|
||||
opacity: _fade,
|
||||
child: SlideTransition(
|
||||
position: _slide,
|
||||
child: SingleChildScrollView(
|
||||
padding: UIConstants.getResponsivePadding(
|
||||
context,
|
||||
mobile: const EdgeInsets.all(UIConstants.spacing16),
|
||||
tablet: const EdgeInsets.all(UIConstants.spacing24),
|
||||
desktop: const EdgeInsets.all(UIConstants.spacing32),
|
||||
),
|
||||
child: Container(
|
||||
width: UIConstants.getResponsiveValue(
|
||||
context,
|
||||
mobile: double.infinity,
|
||||
tablet: 520,
|
||||
desktop: 640,
|
||||
),
|
||||
padding: UIConstants.cardPaddingLarge,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.96),
|
||||
borderRadius:
|
||||
BorderRadius.circular(UIConstants.radius24),
|
||||
border: Border.all(
|
||||
color: Colors.white.withOpacity(0.3), width: 1),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.15),
|
||||
blurRadius: 28,
|
||||
offset: const Offset(0, 16),
|
||||
spreadRadius: 8,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 72,
|
||||
height: 72,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: colorScheme.primary.withOpacity(0.1),
|
||||
border: Border.all(
|
||||
color: Colors.white.withOpacity(0.4),
|
||||
width: 1),
|
||||
),
|
||||
child: Icon(Icons.business_center_outlined,
|
||||
size: 36, color: colorScheme.primary),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: UIConstants.spacing16),
|
||||
Text(
|
||||
"Register Your Company",
|
||||
textAlign: TextAlign.center,
|
||||
style: textTheme.headlineMedium?.copyWith(
|
||||
color: colorScheme.primary,
|
||||
fontWeight: FontWeight.w800,
|
||||
letterSpacing: 0.2,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: UIConstants.spacing8),
|
||||
Text(
|
||||
"Create your workspace and admin account",
|
||||
textAlign: TextAlign.center,
|
||||
style: textTheme.bodyMedium?.copyWith(
|
||||
color: Colors.black.withOpacity(0.65),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: UIConstants.spacing24),
|
||||
|
||||
// Company Name
|
||||
MyCustomTextFormField(
|
||||
controller: _companyNameController,
|
||||
label: "Company Name",
|
||||
prefixIcon: const Icon(Icons.business),
|
||||
validator: TextFieldValidator.validateField,
|
||||
),
|
||||
const SizedBox(height: UIConstants.spacing16),
|
||||
|
||||
// Admin Full Name
|
||||
MyCustomTextFormField(
|
||||
controller: _adminNameController,
|
||||
label: "Admin Name",
|
||||
prefixIcon: const Icon(Iconsax.profile_circle),
|
||||
validator: TextFieldValidator.validateField,
|
||||
),
|
||||
const SizedBox(height: UIConstants.spacing16),
|
||||
|
||||
// Company Email
|
||||
MyCustomTextFormField(
|
||||
controller: _emailController,
|
||||
label: "Company Email",
|
||||
prefixIcon: const Icon(Icons.alternate_email),
|
||||
validator: TextFieldValidator.validateEmail,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
),
|
||||
const SizedBox(height: UIConstants.spacing16),
|
||||
|
||||
// Phone Number
|
||||
MyCustomTextFormField(
|
||||
controller: _phoneController,
|
||||
label: "Admin Phone Number",
|
||||
prefixIcon: const Icon(Icons.phone_outlined),
|
||||
validator: TextFieldValidator.validateField,
|
||||
keyboardType: TextInputType.phone,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
],
|
||||
),
|
||||
const SizedBox(height: UIConstants.spacing16),
|
||||
|
||||
// Workspace/Branch Name
|
||||
MyCustomTextFormField(
|
||||
controller: _workspaceController,
|
||||
label: "Workspace/Branch Name",
|
||||
prefixIcon: const Icon(Icons.location_city),
|
||||
validator: TextFieldValidator.validateField,
|
||||
),
|
||||
|
||||
const SizedBox(height: UIConstants.spacing24),
|
||||
|
||||
// Register Button
|
||||
Consumer<AuthViewModel>(
|
||||
builder: (context, provider, child) {
|
||||
return MyCustomElevatedButton(
|
||||
isLoading: provider.isLoading,
|
||||
child: const Text("Register Company"),
|
||||
onPressed: () async {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
final Map<String, dynamic> signUpData = {
|
||||
"companyName":
|
||||
_companyNameController.text.trim(),
|
||||
"email": _emailController.text.trim(),
|
||||
"mobile": _phoneController.text.trim(),
|
||||
"workspace":
|
||||
_workspaceController.text.trim(),
|
||||
};
|
||||
await Provider.of<AuthViewModel>(context,
|
||||
listen: false)
|
||||
.createAcc(context, signUpData);
|
||||
if (mounted) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SignUpView(
|
||||
email:
|
||||
_emailController.text.trim(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
1167
base_project/lib/view/auth/get_otp.dart
Normal file
1167
base_project/lib/view/auth/get_otp.dart
Normal file
File diff suppressed because it is too large
Load Diff
1007
base_project/lib/view/auth/login.dart
Normal file
1007
base_project/lib/view/auth/login.dart
Normal file
File diff suppressed because it is too large
Load Diff
11
base_project/lib/view/auth/register_acc.dart
Normal file
11
base_project/lib/view/auth/register_acc.dart
Normal file
@@ -0,0 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class RegisterAccView extends StatelessWidget {
|
||||
const RegisterAccView
|
||||
({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Scaffold();
|
||||
}
|
||||
}
|
||||
355
base_project/lib/view/auth/sign_up_view.dart
Normal file
355
base_project/lib/view/auth/sign_up_view.dart
Normal file
@@ -0,0 +1,355 @@
|
||||
import 'package:base_project/core/constants/ui_constants.dart';
|
||||
import 'package:base_project/core/providers/dynamic_theme_provider.dart';
|
||||
import 'package:base_project/routes/route_names.dart';
|
||||
import 'package:base_project/shared/widgets/app_bar/modern_app_bar.dart';
|
||||
import 'package:base_project/shared/widgets/buttons/modern_button.dart';
|
||||
import 'package:base_project/shared/widgets/inputs/modern_text_field.dart';
|
||||
import 'package:base_project/utils/validator/text_feild_validator.dart';
|
||||
import 'package:base_project/view_model/auth/auth_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:iconsax/iconsax.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class SignUpView extends StatefulWidget {
|
||||
final String email;
|
||||
SignUpView({super.key, required this.email});
|
||||
|
||||
@override
|
||||
State<SignUpView> createState() => _SignUpViewState();
|
||||
}
|
||||
|
||||
class _SignUpViewState extends State<SignUpView> with TickerProviderStateMixin {
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
final TextEditingController _firstNameController = TextEditingController();
|
||||
final TextEditingController _lastNameController = TextEditingController();
|
||||
final TextEditingController _phoneController = TextEditingController();
|
||||
final TextEditingController _dobController = TextEditingController();
|
||||
final TextEditingController _passwordController = TextEditingController();
|
||||
final TextEditingController _confirmPasswordController =
|
||||
TextEditingController();
|
||||
final ValueNotifier<bool> _obscurePassword = ValueNotifier<bool>(true);
|
||||
final ValueNotifier<bool> _obscureConfirm = ValueNotifier<bool>(true);
|
||||
|
||||
late AnimationController _animationController;
|
||||
late Animation<double> _fadeAnimation;
|
||||
late Animation<Offset> _slideAnimation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_animationController = AnimationController(
|
||||
duration: UIConstants.durationSlow,
|
||||
vsync: this,
|
||||
);
|
||||
_fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
|
||||
CurvedAnimation(
|
||||
parent: _animationController, curve: UIConstants.curveNormal),
|
||||
);
|
||||
_slideAnimation =
|
||||
Tween<Offset>(begin: const Offset(0, 0.2), end: Offset.zero).animate(
|
||||
CurvedAnimation(
|
||||
parent: _animationController, curve: UIConstants.curveNormal));
|
||||
WidgetsBinding.instance
|
||||
.addPostFrameCallback((_) => _animationController.forward());
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_firstNameController.dispose();
|
||||
_lastNameController.dispose();
|
||||
_phoneController.dispose();
|
||||
_dobController.dispose();
|
||||
_passwordController.dispose();
|
||||
_confirmPasswordController.dispose();
|
||||
_obscurePassword.dispose();
|
||||
_obscureConfirm.dispose();
|
||||
_animationController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isIOS = Theme.of(context).platform == TargetPlatform.iOS;
|
||||
final textTheme = Theme.of(context).textTheme;
|
||||
final size = MediaQuery.sizeOf(context);
|
||||
|
||||
return Consumer<DynamicThemeProvider>(
|
||||
builder: (context, dynamicThemeProvider, child) {
|
||||
final colorScheme = dynamicThemeProvider.getCurrentColorScheme(
|
||||
Theme.of(context).brightness == Brightness.dark,
|
||||
);
|
||||
|
||||
return Scaffold(
|
||||
appBar: ModernAppBar(
|
||||
title: 'Sign Up',
|
||||
automaticallyImplyLeading: true,
|
||||
leading: IconButton(
|
||||
icon: Icon(
|
||||
isIOS ? Icons.arrow_back_ios_new_rounded : Icons.arrow_back),
|
||||
onPressed: () {
|
||||
Navigator.pushNamedAndRemoveUntil(
|
||||
context,
|
||||
RouteNames.loginView,
|
||||
(Route<dynamic> route) => false,
|
||||
);
|
||||
},
|
||||
),
|
||||
showThemeToggle: false,
|
||||
showUserProfile: false,
|
||||
),
|
||||
body: AnimatedBuilder(
|
||||
animation: _animationController,
|
||||
builder: (context, child) {
|
||||
return FadeTransition(
|
||||
opacity: _fadeAnimation,
|
||||
child: SlideTransition(
|
||||
position: _slideAnimation,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
colorScheme.primary.withOpacity(0.1),
|
||||
colorScheme.secondary.withOpacity(0.05),
|
||||
colorScheme.surface,
|
||||
],
|
||||
stops: const [0.0, 0.3, 1.0],
|
||||
),
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
padding: UIConstants.getResponsivePadding(
|
||||
context,
|
||||
mobile: UIConstants.screenPaddingMedium,
|
||||
tablet: UIConstants.screenPaddingLarge,
|
||||
desktop: UIConstants.screenPaddingLarge,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildHeaderCard(context, textTheme, colorScheme),
|
||||
SizedBox(
|
||||
height: UIConstants.getResponsiveSpacing(
|
||||
context,
|
||||
mobile: UIConstants.spacing32,
|
||||
tablet: UIConstants.spacing40,
|
||||
desktop: UIConstants.spacing48,
|
||||
)),
|
||||
_buildFormCard(context, textTheme, colorScheme, size),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeaderCard(
|
||||
BuildContext context, TextTheme textTheme, ColorScheme colorScheme) {
|
||||
return Container(
|
||||
padding: UIConstants.getResponsivePadding(
|
||||
context,
|
||||
mobile: UIConstants.cardPaddingLarge,
|
||||
tablet: UIConstants.cardPaddingLarge,
|
||||
desktop: UIConstants.cardPaddingLarge,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.95),
|
||||
borderRadius: BorderRadius.circular(UIConstants.radius24),
|
||||
border:
|
||||
Border.all(color: colorScheme.surface.withOpacity(0.2), width: 1),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: colorScheme.primary.withOpacity(0.1),
|
||||
blurRadius: 20,
|
||||
offset: const Offset(0, 10),
|
||||
spreadRadius: 5,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
"Create an Account!",
|
||||
textAlign: TextAlign.center,
|
||||
style: textTheme.headlineSmall?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: UIConstants.spacing8),
|
||||
Text(
|
||||
"Join us today, it's quick and easy.",
|
||||
style: textTheme.bodyMedium
|
||||
?.copyWith(color: colorScheme.onSurfaceVariant),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFormCard(BuildContext context, TextTheme textTheme,
|
||||
ColorScheme colorScheme, Size size) {
|
||||
return Form(
|
||||
key: _formKey,
|
||||
child: Container(
|
||||
padding: UIConstants.getResponsivePadding(
|
||||
context,
|
||||
mobile: UIConstants.cardPaddingLarge,
|
||||
tablet: UIConstants.cardPaddingLarge,
|
||||
desktop: UIConstants.cardPaddingLarge,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.95),
|
||||
borderRadius: BorderRadius.circular(UIConstants.radius24),
|
||||
border:
|
||||
Border.all(color: colorScheme.surface.withOpacity(0.2), width: 1),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: colorScheme.primary.withOpacity(0.1),
|
||||
blurRadius: 20,
|
||||
offset: const Offset(0, 10),
|
||||
spreadRadius: 5,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
ModernTextField(
|
||||
label: 'First Name',
|
||||
hint: 'Enter your first name',
|
||||
controller: _firstNameController,
|
||||
prefixIcon: const Icon(Icons.person),
|
||||
validator: TextFieldValidator.validateField,
|
||||
),
|
||||
SizedBox(height: UIConstants.spacing20),
|
||||
ModernTextField(
|
||||
label: 'Last Name',
|
||||
hint: 'Enter your last name',
|
||||
controller: _lastNameController,
|
||||
prefixIcon: const Icon(Iconsax.profile_circle),
|
||||
validator: TextFieldValidator.validateField,
|
||||
),
|
||||
SizedBox(height: UIConstants.spacing20),
|
||||
ModernTextField(
|
||||
label: 'Phone Number',
|
||||
hint: 'Enter your phone number',
|
||||
controller: _phoneController,
|
||||
prefixIcon: const Icon(Icons.phone_outlined),
|
||||
validator: TextFieldValidator.validateField,
|
||||
keyboardType: const TextInputType.numberWithOptions(),
|
||||
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||
),
|
||||
SizedBox(height: UIConstants.spacing20),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
DateTime? pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: DateTime.now(),
|
||||
firstDate: DateTime(1900),
|
||||
lastDate: DateTime.now(),
|
||||
);
|
||||
pickedDate ??= DateTime.now();
|
||||
_dobController.text =
|
||||
"${pickedDate.day}/${pickedDate.month}/${pickedDate.year}";
|
||||
},
|
||||
child: AbsorbPointer(
|
||||
child: ModernTextField(
|
||||
label: 'Date of Birth',
|
||||
hint: 'DD/MM/YYYY',
|
||||
controller: _dobController,
|
||||
prefixIcon: const Icon(Icons.cake_outlined),
|
||||
validator: TextFieldValidator.validateField,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: UIConstants.spacing20),
|
||||
ValueListenableBuilder<bool>(
|
||||
valueListenable: _obscurePassword,
|
||||
builder: (context, value, _) {
|
||||
return ModernTextField(
|
||||
label: 'Password',
|
||||
hint: 'Enter a strong password',
|
||||
controller: _passwordController,
|
||||
prefixIcon: const Icon(Icons.lock_outline),
|
||||
obscureText: value,
|
||||
validator: TextFieldValidator.validatePassword,
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(value ? Icons.visibility_off : Icons.visibility),
|
||||
onPressed: () => _obscurePassword.value = !value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(height: UIConstants.spacing20),
|
||||
ValueListenableBuilder<bool>(
|
||||
valueListenable: _obscureConfirm,
|
||||
builder: (context, value, _) {
|
||||
return ModernTextField(
|
||||
label: 'Confirm Password',
|
||||
hint: 'Re-enter your password',
|
||||
controller: _confirmPasswordController,
|
||||
prefixIcon: const Icon(Icons.lock_outline),
|
||||
obscureText: value,
|
||||
validator: (val) {
|
||||
if (val != _passwordController.text) {
|
||||
return 'Passwords do not match';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(value ? Icons.visibility_off : Icons.visibility),
|
||||
onPressed: () => _obscureConfirm.value = !value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(
|
||||
height: UIConstants.getResponsiveSpacing(
|
||||
context,
|
||||
mobile: UIConstants.spacing32,
|
||||
tablet: UIConstants.spacing40,
|
||||
desktop: UIConstants.spacing48,
|
||||
)),
|
||||
Consumer<AuthViewModel>(
|
||||
builder: (context, provider, child) {
|
||||
return ModernButton(
|
||||
text: 'Sign Up',
|
||||
type: ModernButtonType.primary,
|
||||
size: ModernButtonSize.large,
|
||||
isLoading: provider.isLoading,
|
||||
icon: const Icon(Icons.person_add_alt_1),
|
||||
onPressed: () {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
final Map<String, dynamic> signUpData = {
|
||||
"first_name": _firstNameController.text,
|
||||
"last_name": _lastNameController.text,
|
||||
"email": widget.email,
|
||||
"mob_no": _phoneController.text,
|
||||
"date_of_birth": _dobController.text,
|
||||
"new_password": _passwordController.text,
|
||||
"confirm_password": _confirmPasswordController.text,
|
||||
"usrGrpId": 1,
|
||||
};
|
||||
// provider.signUp(context, signUpData);
|
||||
// Use the new combined registration method
|
||||
provider.signUpWithAccountCreation(context, signUpData);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
21
base_project/lib/view/auth/signup.dart
Normal file
21
base_project/lib/view/auth/signup.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SignupView extends StatelessWidget {
|
||||
const SignupView
|
||||
({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
icon: Icon(Icons.adaptive.arrow_back),
|
||||
),
|
||||
title: const Text("SignUp"),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
331
base_project/lib/view/auth/verify_otp.dart
Normal file
331
base_project/lib/view/auth/verify_otp.dart
Normal file
@@ -0,0 +1,331 @@
|
||||
import 'package:base_project/utils/toast_messages/toast_message_util.dart';
|
||||
import 'package:base_project/view/auth/admin_reg_view.dart';
|
||||
import 'package:base_project/view_model/auth/auth_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinput/pinput.dart';
|
||||
import 'package:base_project/commans/widgets/custome_elevated_button.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:base_project/core/constants/ui_constants.dart';
|
||||
import 'package:base_project/core/providers/dynamic_theme_provider.dart';
|
||||
|
||||
import 'sign_up_view.dart';
|
||||
|
||||
class VerifyOtpView extends StatefulWidget {
|
||||
final String? email;
|
||||
|
||||
const VerifyOtpView({super.key, this.email});
|
||||
|
||||
@override
|
||||
_VerifyOtpViewState createState() => _VerifyOtpViewState();
|
||||
}
|
||||
|
||||
class _VerifyOtpViewState extends State<VerifyOtpView>
|
||||
with TickerProviderStateMixin {
|
||||
final TextEditingController _otpController = TextEditingController();
|
||||
late AnimationController _pageController;
|
||||
late AnimationController _formController;
|
||||
late Animation<double> _fade;
|
||||
late Animation<Offset> _slide;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_pageController = AnimationController(
|
||||
vsync: this, duration: const Duration(milliseconds: 800));
|
||||
_formController = AnimationController(
|
||||
vsync: this, duration: const Duration(milliseconds: 600));
|
||||
_fade = CurvedAnimation(parent: _pageController, curve: Curves.easeInOut);
|
||||
_slide = Tween<Offset>(begin: const Offset(0, 0.2), end: Offset.zero)
|
||||
.animate(
|
||||
CurvedAnimation(parent: _pageController, curve: Curves.easeOut));
|
||||
_pageController.forward();
|
||||
Future.delayed(
|
||||
const Duration(milliseconds: 250), () => _formController.forward());
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_otpController.dispose();
|
||||
_pageController.dispose();
|
||||
_formController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _handleVerifyButtonPress() {
|
||||
// Get the OTP value from the controller
|
||||
final otp = _otpController.text;
|
||||
|
||||
// Validate OTP value
|
||||
if (otp.isEmpty || otp.length < 6) {
|
||||
// Handle invalid OTP
|
||||
print("Invalid OTP: OTP must be 6 digits.");
|
||||
ToastMessageUtil.showToast(
|
||||
message: "Invalid OTP: OTP must be 6 digits.",
|
||||
toastType: ToastType.error);
|
||||
} else {}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<DynamicThemeProvider>(
|
||||
builder: (context, dynamicThemeProvider, child) {
|
||||
final colorScheme = dynamicThemeProvider.getCurrentColorScheme(
|
||||
Theme.of(context).brightness == Brightness.dark,
|
||||
);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: colorScheme.surface,
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
icon: const Icon(Icons.arrow_back_ios_new),
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
title: Text(
|
||||
'Verify OTP',
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
color: colorScheme.primary,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
colorScheme.primary,
|
||||
colorScheme.primary.withOpacity(0.85),
|
||||
colorScheme.secondary.withOpacity(0.65),
|
||||
],
|
||||
stops: const [0.0, 0.6, 1.0],
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: FadeTransition(
|
||||
opacity: _fade,
|
||||
child: SlideTransition(
|
||||
position: _slide,
|
||||
child: Container(
|
||||
width: UIConstants.getResponsiveValue(
|
||||
context,
|
||||
mobile: double.infinity,
|
||||
tablet: 450,
|
||||
desktop: 500,
|
||||
),
|
||||
margin: const EdgeInsets.all(UIConstants.spacing16),
|
||||
padding: UIConstants.cardPaddingLarge,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.95),
|
||||
borderRadius: BorderRadius.circular(UIConstants.radius24),
|
||||
border: Border.all(
|
||||
color: Colors.white.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.15),
|
||||
blurRadius: 25,
|
||||
offset: const Offset(0, 15),
|
||||
spreadRadius: 8,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.verified,
|
||||
color: colorScheme.primary,
|
||||
size: UIConstants.iconSizeXLarge,
|
||||
),
|
||||
const SizedBox(height: UIConstants.spacing16),
|
||||
Text(
|
||||
'Enter the 6-digit code sent to',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(
|
||||
color: colorScheme.onSurface.withOpacity(0.8),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Text(
|
||||
widget.email ?? 'your email',
|
||||
style:
|
||||
Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
color: colorScheme.primary,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: UIConstants.spacing24),
|
||||
|
||||
// Pinput with theming
|
||||
Pinput(
|
||||
length: 6,
|
||||
controller: _otpController,
|
||||
pinAnimationType: PinAnimationType.fade,
|
||||
defaultPinTheme: PinTheme(
|
||||
width: 54,
|
||||
height: 60,
|
||||
textStyle: TextStyle(
|
||||
fontSize: 20,
|
||||
color: colorScheme.primary,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: Border.all(
|
||||
color: colorScheme.primary.withOpacity(0.25)),
|
||||
borderRadius:
|
||||
BorderRadius.circular(UIConstants.radius12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 6),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
focusedPinTheme: PinTheme(
|
||||
width: 56,
|
||||
height: 62,
|
||||
textStyle: TextStyle(
|
||||
fontSize: 20,
|
||||
color: colorScheme.primary,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: Border.all(
|
||||
color: colorScheme.primary, width: 2),
|
||||
borderRadius:
|
||||
BorderRadius.circular(UIConstants.radius12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: colorScheme.primary.withOpacity(0.15),
|
||||
blurRadius: 14,
|
||||
offset: const Offset(0, 8),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
onCompleted: (_) => _handleVerifyButtonPress(),
|
||||
),
|
||||
|
||||
const SizedBox(height: UIConstants.spacing24),
|
||||
|
||||
// Verify button
|
||||
Consumer<AuthViewModel>(
|
||||
builder: (context, provider, child) {
|
||||
return Column(
|
||||
children: [
|
||||
MyCustomElevatedButton(
|
||||
isLoading: provider.isLoading,
|
||||
onPressed: () async {
|
||||
if (_otpController.text.length < 6) {
|
||||
ToastMessageUtil.showToast(
|
||||
message: 'Enter 6-digit OTP',
|
||||
toastType: ToastType.error,
|
||||
);
|
||||
return;
|
||||
}
|
||||
final data = {
|
||||
'email': widget.email,
|
||||
'otp': _otpController.text,
|
||||
};
|
||||
final error =
|
||||
await provider.verifyOtp(context, data);
|
||||
if (error == null && mounted) {
|
||||
Navigator.push(
|
||||
context,
|
||||
// MaterialPageRoute(
|
||||
// builder: (context) =>
|
||||
// AdminRegView()),
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SignUpView(
|
||||
email: widget.email ?? '',
|
||||
),
|
||||
),
|
||||
);
|
||||
} else if (error != null) {
|
||||
// Inline error under the input
|
||||
ScaffoldMessenger.of(context)
|
||||
..hideCurrentSnackBar()
|
||||
..showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(error),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: const Text('Verify OTP'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
const SizedBox(height: UIConstants.spacing16),
|
||||
|
||||
// Resend timer / action
|
||||
Consumer<AuthViewModel>(
|
||||
builder: (context, provider, child) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
provider.isResendEnabled
|
||||
? "Didn't receive code?"
|
||||
: 'Resend in ${provider.start}s',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(
|
||||
color: colorScheme.onSurface
|
||||
.withOpacity(0.7),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
TextButton(
|
||||
onPressed: provider.isResendEnabled
|
||||
? () {
|
||||
provider.resendOtp(
|
||||
context, {'email': widget.email});
|
||||
}
|
||||
: null,
|
||||
child: Text(
|
||||
'Resend',
|
||||
style: TextStyle(
|
||||
color: provider.isResendEnabled
|
||||
? colorScheme.primary
|
||||
: Colors.grey,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user