OnboardingPozoAppv1/flutter_app/lib/screens/mobile_login_screen.dart

475 lines
22 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../theme.dart';
import 'otp_verification_screen.dart';
class MobileLoginScreen extends StatefulWidget {
const MobileLoginScreen({super.key});
@override
State<MobileLoginScreen> createState() => _MobileLoginScreenState();
}
class _MobileLoginScreenState extends State<MobileLoginScreen> {
final TextEditingController _controller = TextEditingController();
bool _isValid = false;
@override
void initState() {
super.initState();
_controller.addListener(_validateInput);
}
void _validateInput() {
setState(() {
_isValid = _controller.text.length == 10;
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final gap = context.responsiveGap;
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Color(0xFFAF101A)),
onPressed: () => Navigator.pop(context),
),
title: Text(
'PozoApp',
style: theme.textTheme.headlineMedium?.copyWith(
color: const Color(0xFFAF101A),
fontWeight: FontWeight.bold,
),
),
centerTitle: false,
elevation: 0,
backgroundColor: Colors.transparent,
),
body: SafeArea(
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 600),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 16),
Expanded(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 1. Progress Bar: Step 2 of 5 with "Mobile Login" label on right
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Step 2 of 5',
style: theme.textTheme.labelMedium?.copyWith(
fontWeight: FontWeight.bold,
color: theme.colorScheme.onSurfaceVariant,
),
),
const Text(
'Mobile Login',
style: TextStyle(
fontFamily: 'Plus Jakarta Sans',
fontSize: 14,
fontWeight: FontWeight.bold,
color: Color(0xFFAF101A),
),
),
],
),
const SizedBox(height: 8),
Row(
children: List.generate(5, (index) {
return Expanded(
child: Container(
height: 6,
margin: EdgeInsets.only(
left: index == 0 ? 0 : 3.0,
right: index == 4 ? 0 : 3.0,
),
decoration: BoxDecoration(
color: index <= 1 ? const Color(0xFFAF101A) : const Color(0xFFE2E2E2),
borderRadius: BorderRadius.circular(100),
),
),
);
}),
),
const SizedBox(height: 16),
// Title Header & Secure Badge Group
Text(
'Welcome Back',
style: theme.textTheme.headlineLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
Text(
'Enter your mobile number to sign in or create a new account.',
style: theme.textTheme.bodyMedium,
),
const SizedBox(height: 16),
// 6. Top Badge: Green "Secure OTP Verification" chip with lock icon
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: const Color(0xFFFAFFD1).withOpacity(0.8),
borderRadius: BorderRadius.circular(100),
border: Border.all(color: const Color(0xFF016619).withOpacity(0.3)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.lock, size: 14, color: Color(0xFF016619)),
const SizedBox(width: 6),
Text(
'Secure OTP Verification',
style: theme.textTheme.labelSmall?.copyWith(
color: const Color(0xFF016619),
fontWeight: FontWeight.bold,
),
),
],
),
),
const SizedBox(height: 16),
// 2. Truecaller Card
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: const Color(0xFFF3F3F3), // light gray background (surface low)
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFE4BEBA).withOpacity(0.5)),
),
child: Column(
children: [
Row(
children: [
Container(
width: 40,
height: 40,
decoration: const BoxDecoration(
color: Color(0xFF005FAF),
shape: BoxShape.circle,
),
child: const Icon(
Icons.verified_user, // blue shield icon
color: Colors.white,
size: 24,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Verify instantly with',
style: theme.textTheme.bodySmall?.copyWith(
color: Colors.grey[600],
),
),
const Text(
'Truecaller',
style: TextStyle(
fontFamily: 'Plus Jakarta Sans',
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
],
),
),
],
),
const SizedBox(height: 20),
const Text(
'+91 98765 43210',
style: TextStyle(
fontFamily: 'Plus Jakarta Sans',
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
const SizedBox(height: 16),
ElevatedButton.icon(
onPressed: () {
_controller.text = '9876543210';
setState(() {
_isValid = true;
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('⚡ Phone verified automatically with Truecaller! Joining PozoApp...'),
backgroundColor: Color(0xFF005FAF),
behavior: SnackBarBehavior.floating,
),
);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const OtpVerificationScreen(
mobileNumber: '9876543210',
),
),
);
},
icon: const Icon(Icons.flash_on, size: 18, color: Colors.white),
label: const Text(
'1-Tap Verification',
style: TextStyle(
fontFamily: 'Inter',
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF005FAF),
foregroundColor: Colors.white,
minimumSize: const Size.fromHeight(56),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), // 8px for buttons
),
elevation: 1,
),
),
const SizedBox(height: 12),
Text(
'No OTP required. Your phone will be verified automatically.',
textAlign: TextAlign.center,
style: theme.textTheme.bodySmall?.copyWith(
color: Colors.grey[600],
height: 1.4,
),
),
],
),
),
SizedBox(height: gap),
// 3. Divider: "Or enter mobile number manually"
Row(
children: [
const Expanded(child: Divider(color: Color(0xFFE4BEBA))),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
'Or enter mobile number manually',
style: theme.textTheme.labelMedium?.copyWith(
color: Colors.grey[600],
fontWeight: FontWeight.w500,
fontSize: 12,
),
),
),
const Expanded(child: Divider(color: Color(0xFFE4BEBA))),
],
),
SizedBox(height: gap),
Text(
'Mobile Number',
style: theme.textTheme.labelSmall?.copyWith(
color: theme.colorScheme.onSurface,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
// 4. Mobile Number field: Country code group on left, number field on right
Container(
height: 56,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: theme.colorScheme.outlineVariant),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.02),
blurRadius: 4,
offset: const Offset(0, 2),
)
],
),
child: Row(
children: [
// Flag and code prefix box
Container(
padding: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
border: Border(
right: BorderSide(color: theme.colorScheme.outlineVariant),
),
),
child: Row(
children: [
// Indian flag representation
Container(
width: 22,
height: 14,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey[300]!, width: 0.5),
),
child: Column(
children: [
Container(height: 4, color: const Color(0xFFFF9933)),
Container(
height: 4,
color: Colors.white,
child: Center(
child: Container(
width: 2,
height: 2,
decoration: const BoxDecoration(
color: Color(0xFF000080),
shape: BoxShape.circle,
),
),
),
),
Container(height: 4, color: const Color(0xFF128807)),
],
),
),
const SizedBox(width: 8),
Text(
'+91',
style: theme.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.bold,
color: theme.colorScheme.onSurface,
),
)
],
),
),
// Input field
Expanded(
child: TextField(
controller: _controller,
keyboardType: TextInputType.phone,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(10),
],
decoration: const InputDecoration(
hintText: '00000 00000',
border: InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
filled: false,
contentPadding: EdgeInsets.symmetric(horizontal: 16),
),
style: theme.textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.bold,
color: theme.colorScheme.onSurface,
),
),
)
],
),
),
SizedBox(height: gap * 2),
],
),
),
),
// 5. Bottom buttons
Column(
mainAxisSize: MainAxisSize.min,
children: [
// Full width red "Send OTP" button
ElevatedButton(
onPressed: _isValid
? () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => OtpVerificationScreen(
mobileNumber: _controller.text,
),
),
);
}
: null,
style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(56),
backgroundColor: _isValid ? const Color(0xFFAF101A) : Colors.grey[200],
foregroundColor: _isValid ? Colors.white : Colors.grey[400],
elevation: _isValid ? 2 : 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), // 8px default button radius
),
),
child: const Text('Send OTP'),
),
SizedBox(height: gap / 2),
// Outline style red "Help / Talk to Support" button with chat icon
OutlinedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('💬 Connecting with Pozo Support... Please wait.'),
behavior: SnackBarBehavior.floating,
),
);
},
style: OutlinedButton.styleFrom(
minimumSize: const Size.fromHeight(56),
side: const BorderSide(color: Color(0xFFAF101A), width: 2),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), // 8px default button radius
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.forum, color: Color(0xFFAF101A), size: 18),
const SizedBox(width: 8),
const Text(
'Help / Talk to Support',
style: TextStyle(
color: Color(0xFFAF101A),
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
],
),
),
],
),
],
),
),
),
),
),
);
}
}