Commit e714661c authored by Sai Srinivas's avatar Sai Srinivas
Browse files

Initial commit of Gen Service app

parent e276e49f
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Gen Service</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>gen_service</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>
#import "GeneratedPluginRegistrant.h"
import Flutter
import UIKit
import XCTest
class RunnerTests: XCTestCase {
func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}
class FetchMobileResponse {
String? otp;
String? error;
String? message;
FetchMobileResponse({this.otp, this.error, this.message});
FetchMobileResponse.fromJson(Map<String, dynamic> json) {
otp = json['otp'];
error = json['error'];
message = json['message'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['otp'] = this.otp;
data['error'] = this.error;
data['message'] = this.message;
return data;
}
}
/// for Otp
class FetchOTPResponse {
String? error;
String? accId;
String? sessionId;
String? message;
FetchOTPResponse({this.error, this.accId, this.sessionId, this.message});
FetchOTPResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
accId = json['acc_id'];
sessionId = json['session_id'];
message = json['message'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['error'] = this.error;
data['acc_id'] = this.accId;
data['session_id'] = this.sessionId;
data['message'] = this.message;
return data;
}
}
\ No newline at end of file
class CommonResponse {
int? error;
int? balance;
String? message;
String? filePath;
CommonResponse({this.error, this.balance, this.message});
CommonResponse.fromJson(Map<String, dynamic> json) {
error = int.tryParse(json['error']?.toString() ?? '');
balance = int.tryParse(json['balance']?.toString() ?? '');
message = json['message']?.toString();
filePath = json['file_path']?.toString();
}
Map<String, dynamic> toJson() {
return {
'error': error,
'balance': balance,
'message': message,
'file_path': filePath,
};
}
}
class DashboardResponse {
String? error;
String? mobNum;
String? userName;
String? userProfile;
String? balanceAmount;
String? balanceType;
List<Orders>? orders;
List<Complaints>? complaints;
String? message;
String? sessionExists;
DashboardResponse(
{this.error,
this.mobNum,
this.userName,
this.userProfile,
this.balanceAmount,
this.balanceType,
this.orders,
this.complaints,
this.message,
this.sessionExists});
DashboardResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
mobNum = json['mob_num'];
userName = json['user_name'];
userProfile = json['user_profile'];
balanceAmount = json['balance_amount'];
balanceType = json['balance_type'];
if (json['orders'] != null) {
orders = <Orders>[];
json['orders'].forEach((v) {
orders!.add(new Orders.fromJson(v));
});
}
if (json['complaints'] != null) {
complaints = <Complaints>[];
json['complaints'].forEach((v) {
complaints!.add(new Complaints.fromJson(v));
});
}
message = json['message'];
sessionExists = json['session_exists'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['error'] = this.error;
data['mob_num'] = this.mobNum;
data['user_name'] = this.userName;
data['user_profile'] = this.userProfile;
data['balance_amount'] = this.balanceAmount;
data['balance_type'] = this.balanceType;
if (this.orders != null) {
data['orders'] = this.orders!.map((v) => v.toJson()).toList();
}
if (this.complaints != null) {
data['complaints'] = this.complaints!.map((v) => v.toJson()).toList();
}
data['message'] = this.message;
data['session_exists'] = this.sessionExists;
return data;
}
}
class Orders {
String? id;
String? hashId;
String? engine;
String? prodName;
String? amc;
String? warranty;
String? productImage;
List<String>? schedule;
Orders(
{this.id,
this.hashId,
this.engine,
this.prodName,
this.amc,
this.warranty,
this.productImage,
this.schedule});
Orders.fromJson(Map<String, dynamic> json) {
id = json['id'];
hashId = json['hash_id'];
engine = json['engine'];
prodName = json['prod_name'];
amc = json['amc'];
warranty = json['warranty'];
productImage = json['productImage'];
schedule = json['schedule'].cast<String>();
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['hash_id'] = this.hashId;
data['engine'] = this.engine;
data['prod_name'] = this.prodName;
data['amc'] = this.amc;
data['warranty'] = this.warranty;
data['productImage'] = this.productImage;
data['schedule'] = this.schedule;
return data;
}
}
class Complaints {
String? id;
String? openStatus;
String? modelName;
String? registredDate;
String? hashId;
String? productName;
String? complaintName;
Complaints(
{this.id,
this.openStatus,
this.modelName,
this.registredDate,
this.hashId,
this.productName,
this.complaintName});
Complaints.fromJson(Map<String, dynamic> json) {
id = json['id'];
openStatus = json['open_status'];
modelName = json['model_name'];
registredDate = json['registred_date'];
hashId = json['hash_id'];
productName = json['product_name'];
complaintName = json['complaint_name'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['open_status'] = this.openStatus;
data['model_name'] = this.modelName;
data['registred_date'] = this.registredDate;
data['hash_id'] = this.hashId;
data['product_name'] = this.productName;
data['complaint_name'] = this.complaintName;
return data;
}
}
class TransactionListResponse {
String? error;
String? balanceAmount;
String? balanceType;
String? totalCredit;
String? totalDebit;
Map<String, List<TransactionItem>>? transactions;
String? message;
TransactionListResponse({
this.error,
this.balanceAmount,
this.balanceType,
this.totalCredit,
this.totalDebit,
this.transactions,
this.message,
});
TransactionListResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
balanceAmount = json['balance_amount'];
balanceType = json['balance_type'];
totalCredit = json['total_credit'];
totalDebit = json['total_debit'];
message = json['message'];
if (json['transactions'] != null) {
transactions = {};
json['transactions'].forEach((key, value) {
transactions![key] = List<TransactionItem>.from(
value.map((v) => TransactionItem.fromJson(v)),
);
});
}
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['error'] = error;
data['balance_amount'] = balanceAmount;
data['balance_type'] = balanceType;
data['total_credit'] = totalCredit;
data['total_debit'] = totalDebit;
data['message'] = message;
if (transactions != null) {
data['transactions'] = transactions!.map((key, value) =>
MapEntry(key, value.map((v) => v.toJson()).toList()));
}
return data;
}
}
class TransactionItem {
String? id;
String? billId;
String? atype;
String? type;
String? datetime;
String? cAmount;
String? dAmount;
String? narration;
TransactionItem({
this.id,
this.billId,
this.atype,
this.type,
this.datetime,
this.cAmount,
this.dAmount,
this.narration,
});
TransactionItem.fromJson(Map<String, dynamic> json) {
id = json['id'];
billId = json['bill_id'];
atype = json['atype'];
type = json['type'];
datetime = json['datetime'];
cAmount = json['c_amount'];
dAmount = json['d_amount'];
narration = json['narration'];
}
Map<String, dynamic> toJson() => {
'id': id,
'bill_id': billId,
'atype': atype,
'type': type,
'datetime': datetime,
'c_amount': cAmount,
'd_amount': dAmount,
'narration': narration,
};
}
import 'package:android_id/android_id.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'dart:io';
import 'package:android_id/android_id.dart';
import '../Models/AuthResponse.dart';
import '../Models/CommonResponse.dart';
import '../Services/api_calling.dart';
class AuthProvider with ChangeNotifier {
bool _isLoading = false;
bool get isLoading => _isLoading;
FetchMobileResponse? _mobileResponse;
FetchMobileResponse? get mobileResponse => _mobileResponse;
FetchOTPResponse? _otpResponse;
FetchOTPResponse? get otpResponse => _otpResponse;
CommonResponse? _logoutResponse;
CommonResponse? get logoutResponse => _logoutResponse;
String _deviceDetails = "";
String _deviceId = "";
String _androidId = 'Unknown';
static const _androidIdPlugin = AndroidId();
String get deviceId => _deviceId;
String get deviceDetails => _deviceDetails;
void _setLoading(bool value) {
_isLoading = value;
notifyListeners();
}
Future<void> initAndroidId() async {
String androidId;
var deviceInfo = DeviceInfoPlugin(); // import 'dart:io'
var androidDeviceInfo = await deviceInfo.androidInfo;
_deviceDetails = await "Version Name: " +
androidDeviceInfo.version.baseOS.toString().trim() +
", " +
"Version Code: " +
androidDeviceInfo.version.codename.toString().trim() +
", " +
"OS Version: " +
androidDeviceInfo.version.codename.toString().trim() +
", SDK Version: " +
androidDeviceInfo.version.sdkInt.toString().trim() +
", Device: " +
androidDeviceInfo.device.toString().trim() +
", Model: " +
androidDeviceInfo.model.toString().trim() +
", Product: " +
androidDeviceInfo.product.toString().trim() +
", Manufacturer: " +
androidDeviceInfo.manufacturer.toString().trim() +
", Brand: " +
androidDeviceInfo.brand.toString().trim() +
", User: " +
androidDeviceInfo.data['user'].toString().trim() +
", Display: " +
androidDeviceInfo.display.toString().trim() +
", Hardware: " +
androidDeviceInfo.hardware.toString().trim() +
", Board: " +
androidDeviceInfo.board.toString().trim() +
", Host: " +
androidDeviceInfo.host.toString().trim() +
", Serial: " +
//androidDeviceInfo.serialNumber.toString().trim() +
", ID: " +
androidDeviceInfo.id.toString().trim() +
", Bootloader: " +
androidDeviceInfo.bootloader.toString().trim() +
", CPU ABI1: " +
androidDeviceInfo.supported64BitAbis.toString().trim() +
", CPU ABI2: " +
androidDeviceInfo.supported64BitAbis.toString().trim() +
", FingerPrint: " +
androidDeviceInfo.fingerprint.toString().trim();
try {
androidId = await _androidIdPlugin.getId() ?? 'Unknown ID';
_deviceId = androidId;
debugPrint("testing" + deviceId);
debugPrint(_deviceDetails.toString());
} on PlatformException {
androidId = 'Failed to get Android ID.';
}
_androidId = androidId;
print(_deviceDetails);
print(_deviceId);
print(_androidId);
notifyListeners();
}
Future<String?> getDevId() async {
var deviceInfo = DeviceInfoPlugin(); // import 'dart:io'
var iosDeviceInfo = await deviceInfo.iosInfo;
_deviceId = iosDeviceInfo.identifierForVendor!;
_deviceDetails = iosDeviceInfo.toString();
notifyListeners();
}
/// 🔹 Fetch OTP by Mobile Number
Future<void> fetchMobile(String mobile) async {
_setLoading(true);
try {
final res = await ApiCalling.fetchRentalMobileApi(mobile);
_mobileResponse = res;
notifyListeners();
} catch (e) {
debugPrint("❌ fetchMobile Provider Error: $e");
} finally {
_setLoading(false);
}
}
/// 🔹 Verify OTP API
Future<void> verifyOtp(String mobile, String otp, ) async {
_setLoading(true);
try {
final res = await ApiCalling.fetchMobileOtpApi(mobile, otp, deviceDetails??"");
_otpResponse = res;
notifyListeners();
} catch (e) {
debugPrint("❌ verifyOtp Provider Error: $e");
} finally {
_setLoading(false);
}
}
/// 🔹 Logout API
Future<void> logout(String accId, String sessionId) async {
_setLoading(true);
try {
final res = await ApiCalling.logoutApi(accId, sessionId);
_logoutResponse = res;
notifyListeners();
} catch (e) {
debugPrint("❌ logout Provider Error: $e");
} finally {
_setLoading(false);
}
}
}
import 'package:flutter/material.dart';
import 'package:gen_service/Services/api_calling.dart';
import '../Models/DashboardResponse.dart';
class DashboardProvider extends ChangeNotifier {
/// Dashboard data
DashboardResponse? _dashboardData;
/// Loading state
bool _isLoading = false;
/// Error message (if any)
String? _errorMessage;
// =======================
// Getters
// =======================
DashboardResponse? get dashboardData => _dashboardData;
bool get isLoading => _isLoading;
String? get errorMessage => _errorMessage;
// =======================
// Fetch Dashboard API
// =======================
Future<void> fetchDashboard(String accId, String sessionId) async {
_isLoading = true;
_errorMessage = null;
notifyListeners();
try {
final response = await ApiCalling.fetchDashboardApi(accId, sessionId);
if (response != null) {
if (response.error == "0") {
_dashboardData = response;
} else {
_errorMessage = response.message ?? "Something went wrong!";
}
} else {
_errorMessage = "No response from server.";
}
} catch (e) {
_errorMessage = "Failed to fetch dashboard: $e";
} finally {
_isLoading = false;
notifyListeners();
}
}
// =======================
// Refresh Dashboard
// =======================
Future<void> refreshDashboard(String accId, String sessionId) async {
await fetchDashboard(accId, sessionId);
}
// =======================
// Clear Data
// =======================
void clear() {
_dashboardData = null;
_errorMessage = null;
_isLoading = false;
notifyListeners();
}
}
import 'package:flutter/cupertino.dart';
import 'package:gen_service/Services/api_calling.dart';
import '../Models/TransactionListResponse.dart';
class TransactionsProvider with ChangeNotifier {
bool _isLoading = false;
String? _errorMessage;
TransactionListResponse? _transactionList;
bool get isLoading => _isLoading;
String? get errorMessage => _errorMessage;
TransactionListResponse? get transactionList => _transactionList;
/// Fetch Transactions from API
Future<void> fetchTransactions(String accId, String sessionId) async {
_isLoading = true;
_errorMessage = null;
notifyListeners();
try {
final response = await ApiCalling.fetchTransactionListApi(accId, sessionId);
if (response != null) {
_transactionList = response;
_errorMessage = null;
} else {
_errorMessage = "No response from server";
}
} catch (e) {
_errorMessage = "Failed to fetch transactions: $e";
}
_isLoading = false;
notifyListeners();
}
/// Clear Data
void clearTransactions() {
_transactionList = null;
_errorMessage = null;
notifyListeners();
}
}
import 'package:flutter/material.dart';
class ThemeProvider extends ChangeNotifier {
bool _isDark = false;
bool get isDark => _isDark;
void toggleTheme() {
_isDark = !_isDark;
notifyListeners();
}
}
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:gen_service/Notifiers/AuthProvider.dart';
import 'package:provider/provider.dart';
import '../../Utility/AdvancedSnackbar.dart';
import '../../Utility/AppColors.dart';
import '../../Utility/CustomSnackbar.dart';
import '../../Utility/SharedpreferencesService.dart';
import 'OTP_Screen.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final TextEditingController _mobileController = TextEditingController();
final _formKey = GlobalKey<FormState>();
bool _isValid = false;
bool _showError = false;
bool _hasUserInteracted = false;
final prefs = SharedPreferencesService.instance;
void _validatePhone(String value) {
setState(() {
_isValid = RegExp(r'^[0-9]{10}$').hasMatch(value);
// Reset error when user clears the field or starts typing
if (value.isEmpty) {
_showError = false;
}
});
}
void _checkValidation() {
setState(() {
_hasUserInteracted = true;
// Only show error if field is not empty and invalid
_showError = _mobileController.text.isNotEmpty && !_isValid;
});
}
Future<void> _login(BuildContext context) async {
// Check validation when button is pressed
_checkValidation();
// If invalid, don't proceed
if (!_isValid) return;
final authProvider = Provider.of<AuthProvider>(context, listen: false);
final mob = _mobileController.text.trim();
await authProvider.fetchMobile(mob);
// Handle response - Check if error is "0" (string comparison)
if (authProvider.mobileResponse != null && authProvider.mobileResponse!.error == "0") {
AnimatedSnackBar.success(
context: context,
title: "OTP Sent",
message: authProvider.mobileResponse?.message ?? "OTP sent to your registered mobile number!",
);
// Navigate to OTP screen after a short delay
Future.delayed(const Duration(milliseconds: 500), () {
if (mounted) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => OtpScreen(mob: mob),
),
);
}
});
} else {
CustomSnackBar.showWarning(
context: context,
title: "Login Failed",
message: authProvider.mobileResponse?.message.toString() ??
"Mobile number not registered or invalid",
);
}
}
@override
Widget build(BuildContext context) {
final authProvider = Provider.of<AuthProvider>(context);
final size = MediaQuery.of(context).size;
return Scaffold(
resizeToAvoidBottomInset: true,
backgroundColor: Colors.blue,
body: Stack(
children: [
/// 🔹 Background image
Container(// here is fixed same we need to do for profile here
width: double.infinity,
height: double.infinity,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/background_png.png"),
fit: BoxFit.cover,
),
),
),
/// 🔹 Main content (scrollable & keyboard-safe)
SafeArea(// here is scrollable
child: LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom, // moves up with keyboard
),
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: IntrinsicHeight(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const SizedBox(height: 80),
/// 🔹 Logo
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(width: 30,),
SvgPicture.asset(
"assets/svg/genesis_logo_2io.svg",
height: 50,
color: Colors.white,
),
]
),
const SizedBox(height: 12),
const Spacer(),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(width: 6,),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Login to",
style: TextStyle(
fontSize: 48,
height: 1,
fontFamily: "PoppinsThin",
fontWeight: FontWeight.w200,
color: Colors.white,
),
),
Text(
"continue",
style: TextStyle(
fontSize: 48,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
],
),
),
],
),
const SizedBox(height: 20),
/// 🔹 Bottom Sheet style area
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24),
),
),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Enter Registered Mobile No.",
style: TextStyle(
fontSize: 14,
color: AppColors.normalText,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 10),
/// 🔹 Mobile Field
TextField(
controller: _mobileController,
keyboardType: TextInputType.phone,
onChanged: _validatePhone,
maxLength: 10,
style: const TextStyle(color: Colors.black, fontFamily: "Poppins",fontSize: 14),
decoration: InputDecoration(
hintText: "Enter Mobile No.",
hintStyle: const TextStyle(color: Colors.grey,fontFamily: "Poppins", fontSize: 14),
filled: true,
fillColor: AppColors.backgroundRegular,
counterText: "", // Remove character counter
contentPadding: const EdgeInsets.symmetric(
vertical: 16, horizontal: 20),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: BorderSide(
color: Colors.white.withOpacity(0.5),
width: 0.5,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: BorderSide(
color: _showError ? Colors.red : Colors.blue,
width: 1
),
),
),
),
// ⚠ Validation message - Only show after button press and if invalid
if (_showError)
const Padding(
padding: EdgeInsets.only(left: 12, top: 8),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
"*Invalid number. Enter your registered number.",
style: TextStyle(
fontFamily: "Poppins",
color: Colors.redAccent,
fontSize: 12,
),
),
),
),
const SizedBox(height: 20),
/// 🔹 Continue Button (Always visible)
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF0086F1),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
),
onPressed: (authProvider.isLoading)
? null
: () => _login(context),
child: const Text(
"Continue",
style: TextStyle(
fontSize: 16,
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
),
),
],
),
),
),
],
),
),
),
);
},
),
),
],
),
);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment