baseproject

This commit is contained in:
Gaurav Kumar
2025-09-06 19:21:52 +05:30
commit 01be9df2ed
254 changed files with 28342 additions and 0 deletions

View 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(),
),
),
);
}
}
},
);
},
),
],
),
),
),
),
),
),
),
),
);
},
);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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();
}
}

View 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);
}
},
);
},
),
],
),
),
);
}
}

View 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"),
),
);
}
}

View 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,
),
),
),
],
);
},
),
],
),
),
),
),
),
),
);
},
);
}
}