login design added
This commit is contained in:
parent
92da28e6db
commit
dbddbed8ce
BIN
assets/login/login-bg.png
Normal file
BIN
assets/login/login-bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.0 MiB |
BIN
assets/login/login-content.png
Normal file
BIN
assets/login/login-content.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 177 KiB |
BIN
assets/login/nhance-partner-logo.png
Normal file
BIN
assets/login/nhance-partner-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
BIN
build/flutter_assets/assets/login/login-bg.png
Normal file
BIN
build/flutter_assets/assets/login/login-bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.0 MiB |
BIN
build/flutter_assets/assets/login/login-content.png
Normal file
BIN
build/flutter_assets/assets/login/login-content.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 177 KiB |
BIN
build/flutter_assets/assets/login/nhance-partner-logo.png
Normal file
BIN
build/flutter_assets/assets/login/nhance-partner-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
@ -0,0 +1,35 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:nhance_app_pwa/customAppBar/toastHelper.dart';
|
||||||
|
import 'package:nhance_app_pwa/models/environment.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
class ApiService {
|
||||||
|
final BuildContext context;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> _makeGetRequest(
|
||||||
|
Uri url, Map<String, String> headers) async {
|
||||||
|
final response = await http.get(url, headers: headers);
|
||||||
|
return _handleResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> _makePostRequest(
|
||||||
|
Uri url, Map<String, String> body, Map<String, String> headers) async {
|
||||||
|
final response = await http.post(url, headers: headers, body: body);
|
||||||
|
return _handleResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> _handleResponse(http.Response response) async {
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
return jsonDecode(response.body);
|
||||||
|
} else if (response.statusCode == 401) {
|
||||||
|
await _clearLocalStorageAndRedirect();
|
||||||
|
return {};
|
||||||
|
} else {
|
||||||
|
throw Exception('Failed to load data');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,10 +1,13 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:nhance_partner/data/utils/validators.dart';
|
import 'package:nhance_partner/data/utils/validators.dart';
|
||||||
|
import 'package:pinput/pinput.dart';
|
||||||
import '../../../core/config/env.dart';
|
import '../../../core/config/env.dart';
|
||||||
import '../../../core/routing/routes.dart';
|
import '../../../core/routing/routes.dart';
|
||||||
import '../../../data/services/auth_service.dart';
|
import '../../../data/services/auth_service.dart';
|
||||||
|
import '../../layouts/responsive_layout.dart';
|
||||||
import '../../themes/indicators/text_field_theme.dart';
|
import '../../themes/indicators/text_field_theme.dart';
|
||||||
|
|
||||||
class LoginScreen extends StatefulWidget {
|
class LoginScreen extends StatefulWidget {
|
||||||
@ -17,151 +20,593 @@ class LoginScreen extends StatefulWidget {
|
|||||||
class _LoginScreenState extends State<LoginScreen> {
|
class _LoginScreenState extends State<LoginScreen> {
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
final TextEditingController _emailController = TextEditingController();
|
final TextEditingController _emailController = TextEditingController();
|
||||||
final TextEditingController _passwordController = TextEditingController();
|
final TextEditingController _PhoneNumberController = TextEditingController();
|
||||||
|
final TextEditingController _otpController = TextEditingController();
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
bool _obscurePassword = true;
|
bool _obscurePassword = true;
|
||||||
|
int stausVal = 1;
|
||||||
|
int selectedIndex = 1;
|
||||||
|
bool enteredEmailOrMobile = false;
|
||||||
|
|
||||||
Future<void> _handleLogin() async {
|
|
||||||
|
// Future<void> _handleLogin() async {
|
||||||
|
// if (!_formKey.currentState!.validate()) return;
|
||||||
|
//
|
||||||
|
// setState(() => _isLoading = true);
|
||||||
|
//
|
||||||
|
// // Simulate API call
|
||||||
|
// await Future.delayed(const Duration(seconds: 2));
|
||||||
|
//
|
||||||
|
// // TODO: Replace with your AuthService
|
||||||
|
// final success =
|
||||||
|
// _emailController.text == "test@example.com" &&
|
||||||
|
// // _passwordController.text == "password";
|
||||||
|
//
|
||||||
|
// // setState(() => _isLoading = false);
|
||||||
|
//
|
||||||
|
// print("Succes - $success");
|
||||||
|
// if (success) {
|
||||||
|
// print("Ckeck 1");
|
||||||
|
// if (mounted) {
|
||||||
|
// print("Ckeck 2");
|
||||||
|
// await AuthService.saveToken('1234');
|
||||||
|
// final dynamic token = AuthService.saveToken('1234');
|
||||||
|
// print('token - $token');
|
||||||
|
//
|
||||||
|
// print('AppRoutes.home');
|
||||||
|
// context.go(AppRoutes.home);
|
||||||
|
// print("Ckeck 3");
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// if (mounted) {
|
||||||
|
// ScaffoldMessenger.of(
|
||||||
|
// context,
|
||||||
|
// ).showSnackBar(const SnackBar(content: Text("Invalid credentials")));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
final defaultPinTheme = PinTheme(
|
||||||
|
width: 56,
|
||||||
|
height: 56,
|
||||||
|
textStyle: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
color: Color.fromRGBO(30, 60, 87, 1),
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: Color.fromRGBO(234, 239, 243, 1)),
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Future<void> sendMobileOrEmailVerify() async {
|
||||||
if (!_formKey.currentState!.validate()) return;
|
if (!_formKey.currentState!.validate()) return;
|
||||||
|
setState(() {
|
||||||
setState(() => _isLoading = true);
|
enteredEmailOrMobile = true;
|
||||||
|
});
|
||||||
// Simulate API call
|
|
||||||
await Future.delayed(const Duration(seconds: 2));
|
|
||||||
|
|
||||||
// TODO: Replace with your AuthService
|
|
||||||
final success =
|
|
||||||
_emailController.text == "test@example.com" &&
|
|
||||||
_passwordController.text == "password";
|
|
||||||
|
|
||||||
setState(() => _isLoading = false);
|
|
||||||
|
|
||||||
print("Succes - $success");
|
|
||||||
if (success) {
|
|
||||||
print("Ckeck 1");
|
|
||||||
if (mounted) {
|
|
||||||
print("Ckeck 2");
|
|
||||||
await AuthService.saveToken('1234');
|
|
||||||
final dynamic token = AuthService.saveToken('1234');
|
|
||||||
print('token - $token');
|
|
||||||
|
|
||||||
print('AppRoutes.home');
|
|
||||||
context.go(AppRoutes.home);
|
|
||||||
print("Ckeck 3");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (mounted) {
|
|
||||||
ScaffoldMessenger.of(
|
|
||||||
context,
|
|
||||||
).showSnackBar(const SnackBar(content: Text("Invalid credentials")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isWeb = MediaQuery.of(context).size.width > 600;
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: Center(
|
backgroundColor: Colors.white,
|
||||||
|
body: LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
if (!ResponsiveLayout.isMobile(context)) {
|
||||||
|
// Web layout
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: const BoxConstraints(maxWidth: 400),
|
|
||||||
padding: const EdgeInsets.all(16.0),
|
|
||||||
decoration: isWeb
|
|
||||||
? BoxDecoration(
|
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
borderRadius: BorderRadius.circular(8),
|
padding: const EdgeInsets.all(40),
|
||||||
boxShadow: [
|
child: Column(
|
||||||
BoxShadow(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
blurRadius: 12,
|
children: [
|
||||||
color: Colors.black.withOpacity(0.1),
|
Image.asset(
|
||||||
|
"assets/login/nhance-partner-logo.png",
|
||||||
|
height: 45,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 40),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Welcome Back",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 50,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Color(0xFF425C5C),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Image.asset(
|
||||||
|
"assets/login/login-content.png",
|
||||||
|
height: 500,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
const Text(
|
||||||
|
'${'"'}Manage vsdv your policies, track quotations, and grow your business all in one place.${'"'}',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
color: Color(0xFF000000),
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Right: Login form
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
// padding: const EdgeInsets.all(40),
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
image: DecorationImage(
|
||||||
|
image: AssetImage("assets/login/login-bg.png"), // <-- your image path
|
||||||
|
fit: BoxFit.cover, // cover entire container
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(40),
|
||||||
|
bottomLeft: Radius.circular(40),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Center( // <-- center vertically + horizontally
|
||||||
|
child: SizedBox(
|
||||||
|
width: 600, // keep fixed width like first design
|
||||||
|
child: _LoginForm(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Mobile layout
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
Image.asset(
|
||||||
|
"assets/login/nhance-partner-logo.png",
|
||||||
|
height: 45,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
Text(
|
||||||
|
"Welcome Back",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Color(0xFF425C5C),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Image.asset(
|
||||||
|
"assets/login/login-content.png",
|
||||||
|
height: 180 ,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 20,horizontal: 50 ),
|
||||||
|
child: Text(
|
||||||
|
'${'"'}Manage vsdv your policies, track quotations, and grow your business all in one place.${'"'}',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 11,
|
||||||
|
color: Color(0xFF000000),
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: MediaQuery.of(context).size.height, // full screen height
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: [Color(0xFF466F6C), Color(0xFF86D5CF)],
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(40),
|
||||||
|
topRight: Radius.circular(40),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: _LoginForm(context),
|
||||||
)
|
)
|
||||||
: null,
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _LoginForm(BuildContext context) {
|
||||||
|
const focusedBorderColor = Colors.white;
|
||||||
|
const fillColor = Color.fromRGBO(243, 246, 249, 0);
|
||||||
|
const borderColor = Color.fromRGBO(23, 171, 144, 0.4);
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Login to your account",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: ResponsiveLayout.isMobile(context) ? 28 : 35,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
"Enter your email or mobile number to receive a one time password.",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: ResponsiveLayout.isMobile(context) ? 14 : 18,
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.w600
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(3),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade50,
|
||||||
|
borderRadius: BorderRadius.circular(50),
|
||||||
|
),
|
||||||
|
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
buildTab("Mobile", 0),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
buildTab("Email", 1),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
Container(
|
||||||
child: Form(
|
child: Form(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.max,
|
||||||
children: [
|
children: [
|
||||||
// App Logo
|
SizedBox(
|
||||||
Image.asset('assets/logo.png', width: 80, height: 80),
|
width: 400,
|
||||||
const SizedBox(height: 20),
|
height: 70,
|
||||||
|
child: Column(
|
||||||
// Environment label
|
children: [
|
||||||
Text(
|
if (stausVal == 0)
|
||||||
"Environment: ${Env.envName.toUpperCase()}",
|
|
||||||
style: const TextStyle(fontSize: 14, color: Colors.grey),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
|
|
||||||
ThemedFormField(
|
ThemedFormField(
|
||||||
hintText: 'Email',
|
hintText: 'Email',
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
validator: (value) => Validators.email(value, "Email"),
|
validator: (value) => Validators.email(value, "Email"),
|
||||||
// imgPath: "MiscIconAssetPath.person",
|
// imgPath: "MiscIconAssetPath.person",
|
||||||
controller: _emailController,
|
controller: _emailController,
|
||||||
// txtwidth: MediaQuery.of(context).size.width * 0.5,
|
|
||||||
// keyboardType: TextInputType.number,
|
|
||||||
// maxLength: 10,
|
|
||||||
// inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
|
||||||
// readOnly: true,
|
|
||||||
),
|
),
|
||||||
|
if (stausVal == 1)
|
||||||
const SizedBox(height: 16),
|
|
||||||
ThemedFormField(
|
ThemedFormField(
|
||||||
hintText: 'Password',
|
hintText: 'Mobile Number',
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
validator: (value) =>
|
validator: (value) => Validators.phone(value, "Phone"),
|
||||||
Validators.passwordlogIn(value, "Password"),
|
|
||||||
// imgPath: "MiscIconAssetPath.person",
|
// imgPath: "MiscIconAssetPath.person",
|
||||||
controller: _passwordController,
|
controller: _PhoneNumberController,
|
||||||
isObscurable: true,
|
|
||||||
// txtwidth: MediaQuery.of(context).size.width * 0.5,
|
// txtwidth: MediaQuery.of(context).size.width * 0.5,
|
||||||
// keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
// maxLength: 10,
|
maxLength: 10,
|
||||||
// inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||||
// readOnly: true,
|
// readOnly: true,
|
||||||
),
|
),
|
||||||
|
// const SizedBox(height: 16),
|
||||||
// Password
|
// ThemedFormField(
|
||||||
const SizedBox(height: 24),
|
// hintText: 'Password',
|
||||||
|
// backgroundColor: Colors.white,
|
||||||
// Login Button
|
// validator: (value) =>
|
||||||
SizedBox(
|
// Validators.passwordlogIn(value, "Password"),
|
||||||
width: double.infinity,
|
// // imgPath: "MiscIconAssetPath.person",
|
||||||
child: ElevatedButton(
|
// controller: _passwordController,
|
||||||
onPressed: _isLoading ? null : _handleLogin,
|
// isObscurable: true,
|
||||||
child: _isLoading
|
// // txtwidth: MediaQuery.of(context).size.width * 0.5,
|
||||||
? const SizedBox(
|
// // keyboardType: TextInputType.number,
|
||||||
height: 20,
|
// // maxLength: 10,
|
||||||
width: 20,
|
// // inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||||
child: CircularProgressIndicator(
|
// // readOnly: true,
|
||||||
strokeWidth: 2,
|
// ),
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(
|
],
|
||||||
Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
: const Text("Login"),
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
if(enteredEmailOrMobile)
|
||||||
|
Container(
|
||||||
|
child: Text('OTP',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: ResponsiveLayout.isMobile(context) ? 14 : 18,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.white
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
const SizedBox(height: 12),
|
if(enteredEmailOrMobile)
|
||||||
|
Container(
|
||||||
// Forgot password link
|
child: Pinput(
|
||||||
TextButton(
|
length: 6,
|
||||||
onPressed: () {
|
defaultPinTheme:
|
||||||
// TODO: Navigate to forgot password screen
|
defaultPinTheme,
|
||||||
|
separatorBuilder:
|
||||||
|
(index) =>
|
||||||
|
const SizedBox(
|
||||||
|
width: 8),
|
||||||
|
showCursor: true,
|
||||||
|
controller: _otpController,
|
||||||
|
// validator: _validatePin,
|
||||||
|
errorPinTheme:
|
||||||
|
defaultPinTheme
|
||||||
|
.copyBorderWith(
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors
|
||||||
|
.redAccent),
|
||||||
|
),
|
||||||
|
hapticFeedbackType:
|
||||||
|
HapticFeedbackType
|
||||||
|
.lightImpact,
|
||||||
|
onCompleted: (pin) {
|
||||||
|
debugPrint(
|
||||||
|
'onCompleted: $pin');
|
||||||
},
|
},
|
||||||
child: const Text("Forgot password?"),
|
onChanged: (value) {
|
||||||
|
debugPrint(
|
||||||
|
'onChanged: $value');
|
||||||
|
},
|
||||||
|
cursor: Column(
|
||||||
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment
|
||||||
|
.end,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin:
|
||||||
|
const EdgeInsets
|
||||||
|
.only(
|
||||||
|
bottom: 9),
|
||||||
|
width: 22,
|
||||||
|
height: 1,
|
||||||
|
color:
|
||||||
|
focusedBorderColor,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
focusedPinTheme:
|
||||||
|
defaultPinTheme
|
||||||
|
.copyWith(
|
||||||
|
decoration:
|
||||||
|
defaultPinTheme
|
||||||
|
.decoration!
|
||||||
|
.copyWith(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius
|
||||||
|
.circular(8),
|
||||||
|
border: Border.all(
|
||||||
|
color:
|
||||||
|
focusedBorderColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
submittedPinTheme:
|
||||||
|
defaultPinTheme
|
||||||
|
.copyWith(
|
||||||
|
decoration:
|
||||||
|
defaultPinTheme
|
||||||
|
.decoration!
|
||||||
|
.copyWith(
|
||||||
|
color: fillColor,
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius
|
||||||
|
.circular(19),
|
||||||
|
border: Border.all(
|
||||||
|
color:
|
||||||
|
focusedBorderColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
SizedBox(
|
||||||
|
width: 400,
|
||||||
|
child: SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 50,
|
||||||
|
child: ElevatedButton(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Color(0xFF436462),
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
sendMobileOrEmailVerify();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
enteredEmailOrMobile ? 'Continue' : 'Request OTP',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: ResponsiveLayout.isMobile(context) ? 18 : 20,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.white
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildTab(String title, int index) {
|
||||||
|
final isSelected = selectedIndex == index + 1;
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
selectedIndex = index + 1;
|
||||||
|
if (selectedIndex == 2) {
|
||||||
|
stausVal = 0;
|
||||||
|
} else {
|
||||||
|
stausVal = 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding:EdgeInsets.only(
|
||||||
|
top: 6.5, bottom: 6.0, left: 40.0, right: 40.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isSelected ? const Color(0xFF436462) : Colors.transparent,
|
||||||
|
boxShadow: isSelected
|
||||||
|
? [
|
||||||
|
BoxShadow(
|
||||||
|
color: isSelected
|
||||||
|
? const Color(0xFF436462)
|
||||||
|
: Colors.transparent, // Grey shadow
|
||||||
|
spreadRadius: 0.2,
|
||||||
|
blurRadius: 1,
|
||||||
|
offset: const Offset(0, 1), // Horizontal, Vertical
|
||||||
),
|
),
|
||||||
|
]
|
||||||
|
: [],
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
child: Text(title,
|
||||||
|
style: GoogleFonts.poppins(
|
||||||
|
fontSize: 12,
|
||||||
|
color: isSelected ? Colors.white : Colors.black,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// @override
|
||||||
|
// Widget build(BuildContext context) {
|
||||||
|
// final isWeb = MediaQuery.of(context).size.width > 600;
|
||||||
|
//
|
||||||
|
// return Scaffold(
|
||||||
|
// body: Center(
|
||||||
|
// child: Container(
|
||||||
|
// constraints: const BoxConstraints(maxWidth: 400),
|
||||||
|
// padding: const EdgeInsets.all(16.0),
|
||||||
|
// decoration: isWeb
|
||||||
|
// ? BoxDecoration(
|
||||||
|
// color: Colors.white,
|
||||||
|
// borderRadius: BorderRadius.circular(8),
|
||||||
|
// boxShadow: [
|
||||||
|
// BoxShadow(
|
||||||
|
// blurRadius: 12,
|
||||||
|
// color: Colors.black.withOpacity(0.1),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// )
|
||||||
|
// : null,
|
||||||
|
// child: Form(
|
||||||
|
// key: _formKey,
|
||||||
|
// child: Column(
|
||||||
|
// mainAxisSize: MainAxisSize.min,
|
||||||
|
// children: [
|
||||||
|
// // App Logo
|
||||||
|
// Image.asset('assets/logo.png', width: 80, height: 80),
|
||||||
|
// const SizedBox(height: 20),
|
||||||
|
//
|
||||||
|
// // Environment label
|
||||||
|
// Text(
|
||||||
|
// "Environment: ${Env.envName.toUpperCase()}",
|
||||||
|
// style: const TextStyle(fontSize: 14, color: Colors.grey),
|
||||||
|
// ),
|
||||||
|
// const SizedBox(height: 20),
|
||||||
|
//
|
||||||
|
// ThemedFormField(
|
||||||
|
// hintText: 'Email',
|
||||||
|
// backgroundColor: Colors.white,
|
||||||
|
// validator: (value) => Validators.email(value, "Email"),
|
||||||
|
// // imgPath: "MiscIconAssetPath.person",
|
||||||
|
// controller: _emailController,
|
||||||
|
// // txtwidth: MediaQuery.of(context).size.width * 0.5,
|
||||||
|
// // keyboardType: TextInputType.number,
|
||||||
|
// // maxLength: 10,
|
||||||
|
// // inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||||
|
// // readOnly: true,
|
||||||
|
// ),
|
||||||
|
//
|
||||||
|
// const SizedBox(height: 16),
|
||||||
|
// ThemedFormField(
|
||||||
|
// hintText: 'Password',
|
||||||
|
// backgroundColor: Colors.white,
|
||||||
|
// validator: (value) =>
|
||||||
|
// Validators.passwordlogIn(value, "Password"),
|
||||||
|
// // imgPath: "MiscIconAssetPath.person",
|
||||||
|
// controller: _passwordController,
|
||||||
|
// isObscurable: true,
|
||||||
|
// // txtwidth: MediaQuery.of(context).size.width * 0.5,
|
||||||
|
// // keyboardType: TextInputType.number,
|
||||||
|
// // maxLength: 10,
|
||||||
|
// // inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||||
|
// // readOnly: true,
|
||||||
|
// ),
|
||||||
|
//
|
||||||
|
// // Password
|
||||||
|
// const SizedBox(height: 24),
|
||||||
|
//
|
||||||
|
// // Login Button
|
||||||
|
// SizedBox(
|
||||||
|
// width: double.infinity,
|
||||||
|
// child: ElevatedButton(
|
||||||
|
// onPressed: _isLoading ? null : _handleLogin,
|
||||||
|
// child: _isLoading
|
||||||
|
// ? const SizedBox(
|
||||||
|
// height: 20,
|
||||||
|
// width: 20,
|
||||||
|
// child: CircularProgressIndicator(
|
||||||
|
// strokeWidth: 2,
|
||||||
|
// valueColor: AlwaysStoppedAnimation<Color>(
|
||||||
|
// Colors.white,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
// : const Text("Login"),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
//
|
||||||
|
// const SizedBox(height: 12),
|
||||||
|
//
|
||||||
|
// // Forgot password link
|
||||||
|
// TextButton(
|
||||||
|
// onPressed: () {
|
||||||
|
// // TODO: Navigate to forgot password screen
|
||||||
|
// },
|
||||||
|
// child: const Text("Forgot password?"),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
|||||||
16
pubspec.lock
16
pubspec.lock
@ -592,6 +592,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.0"
|
version: "6.1.0"
|
||||||
|
pinput:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: pinput
|
||||||
|
sha256: "8a73be426a91fefec90a7f130763ca39772d547e92f19a827cf4aa02e323d35a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.1"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -821,6 +829,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.0"
|
||||||
|
universal_platform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: universal_platform
|
||||||
|
sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@ -47,6 +47,7 @@ dependencies:
|
|||||||
google_fonts: ^6.3.1
|
google_fonts: ^6.3.1
|
||||||
intl: ^0.20.2
|
intl: ^0.20.2
|
||||||
flutter_hooks: ^0.21.3+1
|
flutter_hooks: ^0.21.3+1
|
||||||
|
pinput: ^5.0.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@ -66,11 +67,10 @@ dev_dependencies:
|
|||||||
|
|
||||||
# The following section is specific to Flutter packages.
|
# The following section is specific to Flutter packages.
|
||||||
flutter:
|
flutter:
|
||||||
|
generate: true
|
||||||
# The following line ensures that the Material Icons font is
|
|
||||||
# included with your application, so that you can use the icons in
|
|
||||||
# the material Icons class.
|
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
assets:
|
||||||
|
- assets/login/
|
||||||
|
|
||||||
# To add assets to your application, add an assets section, like this:
|
# To add assets to your application, add an assets section, like this:
|
||||||
# assets:
|
# assets:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user