25-7-28-2

This commit is contained in:
Hamza-Ayed
2025-07-28 12:21:28 +03:00
parent 660d60e1f5
commit 83a97baed1
549 changed files with 109870 additions and 0 deletions

View File

@@ -0,0 +1,977 @@
import 'package:sefer_driver/controller/functions/crud.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/constant/table_names.dart';
import 'package:sefer_driver/controller/auth/captin/register_captin_controller.dart';
import 'package:sefer_driver/controller/functions/ocr_controller.dart';
import 'package:sefer_driver/main.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:sefer_driver/views/widgets/mycircular.dart';
import '../../../constant/links.dart';
import '../../../controller/functions/encrypt_decrypt.dart';
import '../../../controller/functions/gemeni.dart';
class AiPage extends StatelessWidget {
ScanDocumentsByApi scanDocumentsByApi = Get.put(ScanDocumentsByApi());
RegisterCaptainController registerCaptainController =
Get.put(RegisterCaptainController());
@override
Widget build(BuildContext context) {
Get.put(AI());
String text = '';
return MyScafolld(
title: 'Documents check'.tr,
body: [
GetBuilder<AI>(builder: (controller) {
return controller.isLoading
? const MyCircularProgressIndicator()
: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
children: [
// egyptDriverLicense(),
// egyptCarLicenceFront(),
// egyptCarLicenceBack(),
// egyptDriverIDFront(),
// egyptDriverIDBack(),
],
),
);
}),
],
isleading: true);
}
GetBuilder<AI> egyptDriverLicenseWidget() {
return GetBuilder<AI>(
builder: (contentController) => contentController.responseMap.isNotEmpty
? contentController.isloading
? Column(
children: [
const MyCircularProgressIndicator(),
Text(
'We are process picture please wait '.tr,
style: AppStyle.title,
)
],
)
: SizedBox(
height: Get.height * .7,
child: ListView(
children: [
Container(
decoration: AppStyle.boxDecoration1,
// height: Get.height * .4,
child: Padding(
padding: const EdgeInsets.all(5),
child: contentController.responseMap.isEmpty
? Center(
child: Text(
'Capture an Image of Your Drivers License'
.tr,
style: AppStyle.title,
),
)
: Column(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
mainAxisAlignment:
MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'${'Name'.tr} :${(contentController.responseMap['first_name'])}',
style: AppStyle.subtitle,
),
Text(
' ${(contentController.responseMap['last_name'])}',
style: AppStyle.subtitle,
),
],
),
Text(
'${'Name in arabic'.tr}: ${(contentController.responseMap['name_in_arabic'])}',
style: AppStyle.title,
),
Text(
'${'Drivers License Class'.tr}: ${contentController.responseMap['class']}',
style: AppStyle.title,
),
],
),
],
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'${'National Number'.tr}: ${(contentController.responseMap['id'])}',
style: AppStyle.title,
),
// Image.memory(
// scanDocumentsByApi
// .imagePortrait,
// width: 60,
// ),
]),
Text(
'${'Address'.tr}: ${(contentController.responseMap['address'])}',
style: AppStyle.title,
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Date of Birth'.tr}: ${contentController.responseMap['dob']}',
style: AppStyle.title,
),
Text(
'${'Age'.tr} : ${contentController.responseMap['age_in_years']}',
style: AppStyle.title,
),
],
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Expiry Date'.tr}: ${contentController.responseMap['expiration_date']}',
style: DateTime.parse(
contentController
.responseMap[
'expiration_date']
.toString())
.isBefore(
contentController.now)
? AppStyle.title.copyWith(
color: AppColor.redColor)
: AppStyle.title.copyWith(
color: AppColor.greenColor),
),
],
),
],
),
),
),
const SizedBox(
height: 10,
),
DateTime.parse(contentController
.responseMap['expiration_date']
.toString())
.isBefore(contentController.now)
? Text(
'You can\'t continue with us .\nYou should renew Driver license'
.tr,
style: AppStyle.title
.copyWith(color: AppColor.redColor),
)
: MyElevatedButton(
kolor: AppColor.greenColor,
title: 'Lets check Car license '.tr,
onPressed: () => contentController
.getCarLicenseJordanContent()),
const SizedBox(
height: 10,
),
contentController.responseCarLicenseMapJordan.isNotEmpty
? Container(
decoration: AppStyle.boxDecoration,
// height: Get.height * .3,
width: Get.width * .9,
child: Column(
children: [
Text(
'${'Name'.tr} ${contentController.responseCarLicenseMapJordan['name']}',
style: AppStyle.title,
),
Text(
'${'Address'.tr} ${contentController.responseCarLicenseMapJordan['address']}',
style: AppStyle.title,
),
Text(
'${'Car Kind'.tr} ${contentController.responseCarLicenseMapJordan['car_kind']}',
style: AppStyle.title,
),
Text(
'${'Color'.tr} ${contentController.responseCarLicenseMapJordan['car_color']}',
style: AppStyle.title,
),
Text(
'${'Year'.tr} ${contentController.responseCarLicenseMapJordan['car_year']}',
style: AppStyle.title,
),
Text(
'${'Car Plate'.tr} ${contentController.responseCarLicenseMapJordan['car_plate']}',
style: AppStyle.title,
),
Text(
'${'Car Expire'.tr} ${contentController.responseCarLicenseMapJordan['expire_date_of_license']}',
style: contentController
.responseCarLicenseMapJordan
.isNotEmpty
? DateTime.parse(contentController
.responseCarLicenseMapJordan[
'expire_date_of_license']
.toString())
.isBefore(contentController.now)
? AppStyle.title.copyWith(
color: AppColor.redColor)
: AppStyle.title.copyWith(
color: AppColor.greenColor)
: null,
),
],
),
)
: const SizedBox(),
const SizedBox(
height: 10,
),
// DateTime.parse(contentController
// .responseCarLicenseMap[
// 'expire_date_of_license']
// .toString())
// .isBefore(contentController.now)
// ? Text(
// 'You can\'t continue with us .\nYou should renew Car license'
// .tr,
// style: AppStyle.title.copyWith(
// color: AppColor.redColor),
// )
// :
MyElevatedButton(
kolor: AppColor.greenColor,
title: 'Lets check License Back Face'.tr,
onPressed: () => contentController
.generateBackCarLicenseJordanContent()),
const SizedBox(
height: 10,
),
contentController.responseBackCarLicenseMap.isNotEmpty
? Container(
decoration: AppStyle.boxDecoration,
// height: 300,
child: Column(children: [
Text(
'VIN ${contentController.responseBackCarLicenseMap['vin']}',
style: AppStyle.title,
),
Text(
'Fuel Type ${contentController.responseBackCarLicenseMap['fuelType']}',
style: AppStyle.title,
),
Text(
'Insurance Company ${contentController.responseBackCarLicenseMap['insuranceCompany']}',
style: AppStyle.title,
),
Text(
'Policy Number ${contentController.responseBackCarLicenseMap['policyNumber']}',
style: AppStyle.title,
),
Text(
'Insurance Type ${contentController.responseBackCarLicenseMap['insuranceType']}',
style: AppStyle.title,
)
]))
: const SizedBox()
],
),
)
: Positioned(
top: Get.height * .06,
left: Get.width * .051,
right: Get.width * .051,
child: scanDocumentsByApi.isLoading
? Column(
children: [
const MyCircularProgressIndicator(),
Text(
'We are process picture please wait '.tr,
style: AppStyle.title,
)
],
)
: Column(
children: [
Container(
decoration: AppStyle.boxDecoration1,
height: Get.height * .35,
child: Padding(
padding: const EdgeInsets.all(5),
child: Center(
child: InkWell(
onTap: () async {
await CRUD().allMethodForAI(
'name,address,dob,nationalNo,',
AppLink.uploadEgypt,
'idFront'); //egypt
},
child: Text(
'Take Picture Of ID Card'.tr,
style: AppStyle.title,
),
),
)),
),
],
),
),
);
}
GetBuilder<AI> jordanDriverLicenseWidget() {
return GetBuilder<AI>(
builder: (contentController) => contentController.responseMap.isNotEmpty
? Positioned(
top: Get.height * .09,
left: Get.width * .051,
right: Get.width * .051,
child: contentController.isloading
? Column(
children: [
const MyCircularProgressIndicator(),
Text(
'We are process picture please wait '.tr,
style: AppStyle.title,
)
],
)
: SizedBox(
height: Get.height * .7,
child: ListView(
children: [
Container(
decoration: AppStyle.boxDecoration,
// height: Get.height * .4,
child: Padding(
padding: const EdgeInsets.all(5),
child: contentController.responseMap.isEmpty
? Center(
child: Text(
'There is no data yet.'.tr,
style: AppStyle.title,
),
)
: Column(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
mainAxisAlignment:
MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'${'Name'.tr} :${contentController.responseMap['first_name']}',
style: AppStyle.subtitle,
),
Text(
' ${contentController.responseMap['last_name']}',
style: AppStyle.subtitle,
),
],
),
Text(
'${'Name in arabic'.tr}: ${contentController.responseMap['name_in_arabic']}',
style: AppStyle.title,
),
Text(
'${'Drivers License Class'.tr}: ${contentController.responseMap['class']}',
style: AppStyle.title,
),
],
),
],
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'${'National Number'.tr}: ${contentController.responseMap['id']}',
style: AppStyle.title,
),
// Image.memory(
// scanDocumentsByApi
// .imagePortrait,
// width: 60,
// ),
]),
Text(
'${'Address'.tr}: ${contentController.responseMap['address']}',
style: AppStyle.title,
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Date of Birth'.tr}: ${contentController.responseMap['dob']}',
style: AppStyle.title,
),
Text(
'${'Age'.tr} : ${contentController.responseMap['age_in_years']}',
style: AppStyle.title,
),
],
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Expiry Date'.tr}: ${contentController.responseMap['expiration_date']}',
style: DateTime.parse(
contentController
.responseMap[
'expiration_date']
.toString())
.isBefore(
contentController.now)
? AppStyle.title.copyWith(
color: AppColor.redColor)
: AppStyle.title.copyWith(
color:
AppColor.greenColor),
),
],
),
],
),
),
),
const SizedBox(
height: 10,
),
DateTime.parse(contentController
.responseMap['expiration_date']
.toString())
.isBefore(contentController.now)
? Text(
'You can\'t continue with us .\nYou should renew Driver license'
.tr,
style: AppStyle.title
.copyWith(color: AppColor.redColor),
)
: MyElevatedButton(
kolor: AppColor.greenColor,
title: 'Lets check Car license '.tr,
onPressed: () => contentController
.getTextFromCard(
'''Extract the following information from the front face of the car license card in Jordan:
* name
* Address
* Vehicle type
* car_kind
* car_color
* Vehicle category
* car_year
* car_plate
* Registration type
* Usage type
* expire_date_of_license
Output the extracted information in the following JSON formate and make date format like YYYY-MM-DD''')),
const SizedBox(
height: 10,
),
contentController
.responseCarLicenseMapJordan.isNotEmpty
? Container(
decoration: AppStyle.boxDecoration,
// height: Get.height * .3,
width: Get.width * .9,
child: Column(
children: [
Text(
'${'Name'.tr} ${contentController.responseCarLicenseMapJordan['name']}',
style: AppStyle.title,
),
Text(
'${'Address'.tr} ${contentController.responseCarLicenseMapJordan['address']}',
style: AppStyle.title,
),
Text(
'${'Car Kind'.tr} ${contentController.responseCarLicenseMapJordan['car_kind']}',
style: AppStyle.title,
),
Text(
'${'Color'.tr} ${contentController.responseCarLicenseMapJordan['car_color']}',
style: AppStyle.title,
),
Text(
'${'Year'.tr} ${contentController.responseCarLicenseMapJordan['car_year']}',
style: AppStyle.title,
),
Text(
'${'Car Plate'.tr} ${contentController.responseCarLicenseMapJordan['car_plate']}',
style: AppStyle.title,
),
Text(
'${'ُExpire Date'.tr} ${contentController.responseCarLicenseMapJordan['expire_date_of_license']}',
style: contentController
.responseCarLicenseMapJordan
.isNotEmpty
? DateTime.parse(contentController
.responseCarLicenseMapJordan[
'expire_date_of_license']
.toString())
.isBefore(
contentController.now)
? AppStyle.title.copyWith(
color: AppColor.redColor)
: AppStyle.title.copyWith(
color: AppColor.greenColor)
: null,
),
],
),
)
: const SizedBox(),
const SizedBox(
height: 10,
),
//todo temporary
// DateTime.parse(contentController
// .responseCarLicenseMap[
// 'expire_date_of_license']
// .toString())
// .isBefore(contentController.now)
// ? Text(
// 'You can\'t continue with us .\nYou should renew Car license'
// .tr,
// style: AppStyle.title.copyWith(
// color: AppColor.redColor),
// )
// :
MyElevatedButton(
kolor: AppColor.greenColor,
title: 'Lets check License Back Face'.tr,
onPressed: () =>
contentController.getTextFromCard(
'write json output from extracting car license back face for these key ,vin,fuelType,passengerType,curbWeight,insuranceCompany,policyNumber,notes,insuranceType and output it json .dont add data else this image',
)),
const SizedBox(
height: 10,
),
contentController.responseBackCarLicenseMap.isNotEmpty
? Container(
decoration: AppStyle.boxDecoration,
// height: 300,
child: Column(children: [
Text(
'VIN ${contentController.responseBackCarLicenseMap['vin']}',
style: AppStyle.title,
),
Text(
'Fuel Type ${contentController.responseBackCarLicenseMap['fuelType']}',
style: AppStyle.title,
),
Text(
'Insurance Company ${contentController.responseBackCarLicenseMap['insuranceCompany']}',
style: AppStyle.title,
),
Text(
'Policy Number ${contentController.responseBackCarLicenseMap['policyNumber']}',
style: AppStyle.title,
),
Text(
'Insurance Type ${contentController.responseBackCarLicenseMap['insuranceType']}',
style: AppStyle.title,
)
]))
: const SizedBox()
// MyElevatedButton(
// title: 'Detect Your Face '.tr,
// onPressed: () => scanDocumentsByApi
// .checkMatchFaceApi(),
// ),
// scanDocumentsByApi.res.isEmpty
// ? const SizedBox()
// : scanDocumentsByApi.res['data']
// ['result']
// .toString() ==
// 'Same'
// ? MyElevatedButton(
// onPressed: () async {
// await registerCaptainController
// .register();
// await registerCaptainController
// .addLisence();
// // await scanDocumentsByApi
// // .uploadImagePortrate();
// },
// title:
// 'Go to next step\nscan Car License.'
// .tr,
// kolor: AppColor.greenColor,
// )
// : const SizedBox(),
// MyElevatedButton(
// title: 'get sql data',
// kolor: AppColor.yellowColor,
// onPressed: () {
// sql.deleteAllData(
// TableName.faceDetectTimes);
// sql
// .getAllData(
// TableName.faceDetectTimes)
// value[0]['faceDetectTimes']));
// },
// ),
],
),
),
)
: Positioned(
top: Get.height * .06,
left: Get.width * .051,
right: Get.width * .051,
child: scanDocumentsByApi.isLoading
? Column(
children: [
const MyCircularProgressIndicator(),
Text(
'We are process picture please wait '.tr,
style: AppStyle.title,
)
],
)
: Column(
children: [
Container(
decoration: AppStyle.boxDecoration,
height: Get.height * .35,
child: Padding(
padding: const EdgeInsets.all(5),
child: Center(
child: Text(
'There is no data yet.'.tr,
style: AppStyle.title,
),
)),
),
],
),
),
);
}
GetBuilder<ScanDocumentsByApi> usaDriverLicensWidget() {
return GetBuilder<ScanDocumentsByApi>(
builder: (scanDocumentsByApi) => scanDocumentsByApi.responseMap.isNotEmpty
? Positioned(
top: Get.height * .06,
left: Get.width * .051,
right: Get.width * .051,
child: scanDocumentsByApi.isLoading
? Column(
children: [
const MyCircularProgressIndicator(),
Text(
'We are process picture please wait '.tr,
style: AppStyle.title,
)
],
)
: Column(
children: [
Container(
decoration: AppStyle.boxDecoration,
height: Get.height * .4,
child: Padding(
padding: const EdgeInsets.all(5),
child: scanDocumentsByApi.responseMap.isEmpty
? Center(
child: Text(
'There is no data yet.'.tr,
style: AppStyle.title,
),
)
: Column(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
mainAxisAlignment:
MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
'${'Name :'.tr}${scanDocumentsByApi.name}',
style: AppStyle.subtitle,
),
Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'${'Drivers License Class: '.tr}${scanDocumentsByApi.licenseClass}',
style: AppStyle.title,
),
Image.memory(
scanDocumentsByApi
.imageSignature,
width: 100,
height: 30,
),
],
),
],
),
],
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Document Number: '.tr}${scanDocumentsByApi.documentNo}',
style: AppStyle.title,
),
Image.memory(
scanDocumentsByApi.imagePortrait,
width: 60,
),
]),
Text(
'${'Address: '.tr}${scanDocumentsByApi.address}',
style: AppStyle.title,
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Height: '.tr}${scanDocumentsByApi.height}',
style: AppStyle.subtitle,
),
Text(
'Postal Code: ${scanDocumentsByApi.postalCode}',
style: AppStyle.subtitle,
),
Text(
'Sex: ${scanDocumentsByApi.sex}',
style: AppStyle.subtitle,
),
],
),
Text(
'Territorial Code: ${scanDocumentsByApi.stateCode}',
style: AppStyle.subtitle,
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Expiry Date: '.tr}${scanDocumentsByApi.expireDate}',
style: DateTime.parse(
scanDocumentsByApi
.responseMap['data']
['ocr']
['dateOfExpiry']
.toString())
.isBefore(
scanDocumentsByApi.now)
? AppStyle.title.copyWith(
color: AppColor.redColor)
: AppStyle.title.copyWith(
color: AppColor.greenColor),
),
Text(
'${'Date of Birth: '.tr}${scanDocumentsByApi.dob}',
style: AppStyle.title,
),
],
),
],
),
),
),
DateTime.parse(scanDocumentsByApi.responseMap['data']
['ocr']['dateOfExpiry']
.toString())
.isBefore(scanDocumentsByApi.now)
? Text(
'You can\'t continue with us .\nYou should renew Driver license',
style: AppStyle.title
.copyWith(color: AppColor.redColor),
)
: MyElevatedButton(
title: 'Detect Your Face '.tr,
onPressed: () =>
scanDocumentsByApi.checkMatchFaceApi(),
),
scanDocumentsByApi.res.isEmpty
? const SizedBox()
: scanDocumentsByApi.res['data']['result']
.toString() ==
'Same'
? MyElevatedButton(
onPressed: () async {
await registerCaptainController
.register();
await registerCaptainController
.addLisence();
// await scanDocumentsByApi
// .uploadImagePortrate();
},
title:
'Go to next step\nscan Car License.'.tr,
kolor: AppColor.greenColor,
)
: const SizedBox(),
MyElevatedButton(
title: 'get sql data',
kolor: AppColor.yellowColor,
onPressed: () {
sql.deleteAllData(TableName.faceDetectTimes);
sql.getAllData(TableName.faceDetectTimes);
},
)
],
),
)
: Positioned(
top: Get.height * .06,
left: Get.width * .051,
right: Get.width * .051,
child: scanDocumentsByApi.isLoading
? Column(
children: [
const MyCircularProgressIndicator(),
Text(
'We are process picture please wait '.tr,
style: AppStyle.title,
)
],
)
: Column(
children: [
Container(
decoration: AppStyle.boxDecoration,
height: Get.height * .35,
child: Padding(
padding: const EdgeInsets.all(5),
child: Center(
child: Text(
'There is no data yet.'.tr,
style: AppStyle.title,
),
)),
),
],
),
),
);
}
}
class EgyptDocuments extends StatelessWidget {
const EgyptDocuments({
super.key,
required this.contentController,
});
final AI contentController;
@override
Widget build(BuildContext context) {
return Positioned(
top: 3,
left: Get.width * .1,
right: Get.width * .1,
child: MyElevatedButton(
title: 'Take Picture Of ID Card'.tr, //egypt
onPressed: () async {
await CRUD().allMethodForAI('name,address,dob,nationalNo,',
AppLink.uploadEgypt, 'idFront'); //egypt
},
));
}
}
class JordanDocumants extends StatelessWidget {
const JordanDocumants({
super.key,
required this.contentController,
});
final AI contentController;
@override
Widget build(BuildContext context) {
return Positioned(
top: 3,
left: Get.width * .1,
right: Get.width * .1,
child: MyElevatedButton(
title: 'Take Picture Of Driver License Card'.tr,
onPressed: () {
contentController.getDriverLicenseJordanContent();
},
));
}
}
class UsaAiDocuments extends StatelessWidget {
const UsaAiDocuments({
super.key,
required this.scanDocumentsByApi,
});
final ScanDocumentsByApi scanDocumentsByApi;
@override
Widget build(BuildContext context) {
return Positioned(
top: 3,
left: Get.width * .2,
right: Get.width * .2,
child: MyElevatedButton(
title: 'Take Picture Of ID Card'.tr,
onPressed: () {
scanDocumentsByApi.scanDocumentsByApi();
},
));
}
}

View File

@@ -0,0 +1,83 @@
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import '../../../../constant/colors.dart';
import '../../../../constant/style.dart';
import '../../../../controller/functions/camer_controller.dart';
import '../../../../controller/functions/ocr_controller.dart';
import '../../../widgets/my_scafold.dart';
class CameraLisencePage extends StatelessWidget {
CameraLisencePage.CameraLicensePage({super.key});
final CameraClassController cameraClassController =
Get.put(CameraClassController());
@override
Widget build(BuildContext context) {
return MyScafolld(
title: 'Scan Driver License'.tr,
body: [
Column(children: [
Text(
'Please put your licence in these border'.tr,
style: AppStyle.title.copyWith(color: AppColor.greenColor),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
child: GetBuilder<CameraClassController>(
builder: (cameraClassController) =>
cameraClassController.isCameraInitialized
? Stack(
children: [
Container(
decoration: AppStyle.boxDecoration,
child: CameraPreview(
cameraClassController.cameraController,
),
),
Positioned(
top: Get.height * .1,
right: 5,
left: 5,
child: Container(
height: Get.width * 3 / 4,
width: Get.width * .9,
decoration: BoxDecoration(
// color: AppColor.blueColor,
border: Border.all(
color: AppColor.yellowColor, width: 2),
),
),
),
],
)
: Container(
decoration: AppStyle.boxDecoration,
height: Get.width * 3 / 4,
width: Get.width,
child: Center(
child: Text(
'Camera not initialized yet',
style: AppStyle.title,
),
),
),
),
),
const SizedBox(
height: 20,
),
MyElevatedButton(
title: 'Take Image'.tr,
onPressed: () {
ScanDocumentsByApi().scanDocumentsByApi();
},
)
]),
],
isleading: true,
);
}
}

View File

@@ -0,0 +1,169 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/controller/functions/ocr_controller.dart';
import '../../../constant/style.dart';
import '../../../controller/auth/captin/ml_google_doc.dart';
import '../../../controller/auth/captin/register_captin_controller.dart';
import '../../widgets/elevated_btn.dart';
import '../../widgets/my_scafold.dart';
// class CarLicensePage extends StatelessWidget {
// CarLicensePage({super.key});
// // CarRegistrationRecognizerController carRegistrationRecognizerController =
// // Get.put(CarRegistrationRecognizerController());
// RegisterCaptainController registerCaptainController =
// Get.put(RegisterCaptainController());
// @override
// Widget build(BuildContext context) {
// Get.find<ScanDocumentsByApi>().uploadImagePortrate();
// return MyScafolld(
// title: 'Car License Card'.tr,
// body: [
// Positioned(
// top: 3,
// left: Get.width * .2,
// right: Get.width * .2,
// child: MyElevatedButton(
// title: 'Take Picture Of ID Card'.tr,
// onPressed: () async {
// //0vQRyaYYDWpsv73A5CZOknseK7S2sgwE
// //3vQRyaYYSWpmv69A58ZOkxmeK6M1mgwEDlXrXlBl
// //0pALdqDDYHvzp73Q59SIgbzjG7Z2zkhJXr
// // String? visionApi = AK.serverPHP;
// await carRegistrationRecognizerController.scanText();
// },
// )),
// Positioned(
// top: 50,
// child: SizedBox(
// height: Get.height * .6,
// width: Get.width,
// child: buildImageWithBoundingBoxes(),
// ),
// ),
// Positioned(
// bottom: Get.height * .2,
// left: Get.width * .2,
// right: Get.width * .2,
// child: MyElevatedButton(
// title: 'Register'.tr,
// onPressed: () async {
// // registerCaptainController.addLisence();
// // registerCaptainController.register();
// registerCaptainController.addRegisrationCarForDriver(
// carRegistrationRecognizerController.extracted['vin'],
// carRegistrationRecognizerController.extracted['make'],
// carRegistrationRecognizerController.extracted['model'],
// carRegistrationRecognizerController.extracted['year'],
// carRegistrationRecognizerController.extracted['color'],
// carRegistrationRecognizerController.extracted['owner'],
// carRegistrationRecognizerController
// .extracted['expiration_date'],
// carRegistrationRecognizerController
// .extracted['registration_date'],
// );
// },
// )),
// ],
// isleading: true);
// }
// }
// Widget buildImageWithBoundingBoxes() {
// Get.put(CarRegistrationRecognizerController());
// return GetBuilder<CarRegistrationRecognizerController>(
// builder: (carRegistrationRecognizerController) =>
// carRegistrationRecognizerController.image == null ||
// carRegistrationRecognizerController.extracted.isEmpty
// ? Center(
// child: Text(
// 'No image selected yet'.tr,
// style: AppStyle.headTitle2,
// ))
// : Column(
// children: [
// SizedBox(
// width: Get.width * .8,
// height: Get.width * .5,
// child: Image.file(
// File(carRegistrationRecognizerController
// .croppedFile!.path),
// // fit: BoxFit.fill,
// )),
// const SizedBox(
// height: 20,
// ),
// Container(
// decoration: AppStyle.boxDecoration,
// height: Get.width * .5,
// width: Get.width * .9,
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// children: [
// Text(
// '${'Made :'.tr}${carRegistrationRecognizerController.extracted['make']}',
// style: AppStyle.title,
// ),
// Text(
// '${'model :'.tr}${carRegistrationRecognizerController.extracted['model']}',
// style: AppStyle.title,
// ),
// ],
// ),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// children: [
// Text(
// '${'VIN :'.tr}${carRegistrationRecognizerController.extracted['vin']}',
// style: AppStyle.title,
// ),
// Text(
// '${'year :'.tr}${carRegistrationRecognizerController.extracted['year']}',
// style: AppStyle.title,
// ),
// ],
// ),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// children: [
// Column(
// children: [
// Text(
// 'expiration date :${carRegistrationRecognizerController.extracted['expiration_date']}',
// style: AppStyle.title,
// ),
// Text(
// 'registration date :${carRegistrationRecognizerController.extracted['registration_date']}',
// style: AppStyle.title,
// ),
// ],
// ),
// Text(
// 'color :${carRegistrationRecognizerController.extracted['color']}',
// style: AppStyle.title,
// ),
// ],
// ),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// children: [
// Text(
// 'owner :${carRegistrationRecognizerController.extracted['owner']}',
// style: AppStyle.title,
// ),
// ],
// ),
// ],
// ),
// )
// ],
// ));
// }

View File

@@ -0,0 +1,80 @@
import 'dart:convert';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
class GeminiEgypt extends GetxController {
Map<String, dynamic> responseIdCardDriverEgypt = {};
String? responseIdCardDriverEgypt1;
Future geminiAiExtraction(String prompt, payload) async {
var requestBody = jsonEncode({
'contents': [
{
'parts': [
// {
// 'inlineData': {
// 'mimeType': 'image/jpeg',
// 'data': imageData,
// },
// },
{
'text': """
$payload
$prompt ,and make dates format like year-month-day"""
},
],
},
],
'generationConfig': {
'temperature': 0.4,
'topK': 32,
'topP': 1,
'maxOutputTokens': 4096,
'stopSequences': [],
},
'safety_settings': [
{"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"},
{"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"},
{
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
"threshold": "BLOCK_NONE"
},
{
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
"threshold": "BLOCK_NONE"
},
]
});
final response = await http.post(
Uri.parse(
// 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro-vision:generateContent?key=${AK.geminiApi}'),
'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-latest:generateContent?key=AIzaSyCyoLcSkDzK5_SMe00nhut56SSXWPR074w'),
headers: {'Content-Type': 'application/json'},
body: requestBody,
);
if (response.statusCode == 200) {
var responseData = jsonDecode(response.body);
// Process the responseData as needed
var result = responseData['candidates'][0]['content']['parts'][0]['text'];
RegExp regex = RegExp(r"```json([^`]*)```");
String? jsonString =
regex.firstMatch(responseData.toString())?.group(1)?.trim();
if (jsonString != null) {
// Convert the JSON object to a String
jsonString = jsonEncode(json.decode(jsonString));
responseIdCardDriverEgypt1 = jsonString;
responseIdCardDriverEgypt = jsonDecode(responseIdCardDriverEgypt1!);
update();
return responseIdCardDriverEgypt;
} else {}
// Rest of your code...
} else {}
}
}

View File

@@ -0,0 +1,221 @@
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/controller/auth/captin/register_captin_controller.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:sefer_driver/views/widgets/my_textField.dart';
import 'package:sefer_driver/views/widgets/mycircular.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../Rate/rate_app_page.dart';
class SmsSignupEgypt extends StatelessWidget {
SmsSignupEgypt({super.key});
@override
Widget build(BuildContext context) {
Get.put(RegisterCaptainController());
return MyScafolld(
title: 'Phone Check'.tr,
body: [
GetBuilder<RegisterCaptainController>(
builder: (registerCaptainController) {
return ListView(
// mainAxisAlignment: MainAxisAlignment.center,
children: [
// Logo at the top
Padding(
padding: const EdgeInsets.only(bottom: 20.0),
child: Image.asset(
'assets/images/logo.gif', // Make sure you have a logo image in your assets folder
height: 100,
),
),
// Message to the driver
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Text(
'We need your phone number to contact you and to help you receive orders.'
.tr,
textAlign: TextAlign.center,
style: AppStyle.title,
),
),
// Enter phone number text
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Text(
'Enter your phone number'.tr,
textAlign: TextAlign.center,
style: AppStyle.title,
),
),
// Phone number input field
Padding(
padding: const EdgeInsets.all(16.0),
child: !registerCaptainController.isSent
? Form(
key: registerCaptainController.formKey3,
child: MyTextForm(
controller:
registerCaptainController.phoneController,
label: 'Enter your phone number'.tr,
hint: 'Enter your phone number'.tr,
type: TextInputType.phone),
)
: Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
registerCaptainController.phoneController.text,
style: AppStyle.title,
),
),
)),
const SizedBox(
height: 10,
),
Padding(
padding: const EdgeInsets.all(16.0),
child: registerCaptainController.isSent
? Form(
key: registerCaptainController.formKey3,
child: MyTextForm(
controller: registerCaptainController.verifyCode,
label: '5 digit'.tr,
hint: '5 digit'.tr,
type: TextInputType.number),
)
: const SizedBox()),
// Submit button
registerCaptainController.isLoading
? const MyCircularProgressIndicator()
: Padding(
padding: const EdgeInsets.all(16.0),
child: MyElevatedButton(
onPressed: () async {
!registerCaptainController.isSent
? await registerCaptainController.sendOtpMessage()
: await registerCaptainController.verifySMSCode();
},
title: 'Submit'.tr,
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: MyElevatedButton(
kolor: AppColor.yellowColor,
title: "Rate Our App".tr,
onPressed: () {
Get.to(RatingScreen());
}),
),
// IconButton(
// onPressed: () async {
// // final plainText =
// // 'https://server.sefer.live/sefer.click/sefer';
// // debugPrint('Plain Text: $plainText');
// // Encrypt the data
// // final encryptedData = encryptionHelper.encryptData(plainText);
// // debugPrint('Encrypted: $encryptedData');
// // Decrypt the data
// // final decryptedData = encryptionHelper.decryptData(
// // '2unGmj8jSMFBfxqH8+GN'); // Use the encryptedData variable
// // debugPrint('Decrypted: $decryptedData');
// // box.remove('DriversSecure');
// var drivers0 = await CRUD().get(
// link:
// 'https://server.sefer.live/sefer.click/sefer/auth/captin/getAllDriverSecure.php',
// payload: {});
// var decodedDriver;
// if (drivers0 != 'failure') {
// decodedDriver = jsonDecode(drivers0);
// // // // box.write('DriversSecure', decodedDriver['message']);
// }
// var drivers = decodedDriver['message'];
// Log.print('drivers.length: ${drivers.length}');
// for (var i = 0; i < drivers.length; i++) {
// Log.print('id: ${drivers[i]['id']}');
// var payload = {
// "phone": encryptionHelper
// .encryptData(drivers[i]['phone'].toString()),
// "email": encryptionHelper
// .encryptData(drivers[i]['email'].toString()),
// "gender": encryptionHelper
// .encryptData(drivers[i]['gender'] ?? 'unknown'),
// "birthdate": encryptionHelper
// .encryptData(drivers[i]['birthdate'].toString()),
// "first_name": encryptionHelper
// .encryptData(drivers[i]['first_name'].toString()),
// "last_name": encryptionHelper
// .encryptData(drivers[i]['last_name'].toString()),
// "sosPhone": encryptionHelper
// .encryptData(drivers[i]['sosPhone'].toString()),
// // "name_english": encryptionHelper
// // .encryptData(drivers[i]['name_english'].toString()),
// // "last_name": encryptionHelper
// // .encryptData(drivers[i]['last_name'].toString()),
// // "sosPhone": encryptionHelper
// // .encryptData(drivers[i]['sosPhone'].toString()),
// // "address": encryptionHelper
// // .encryptData(drivers[i]['address'].toString()),
// // "card_id": encryptionHelper
// // .encryptData(drivers[i]['card_id'].toString()),
// // "occupation": encryptionHelper
// // .encryptData(drivers[i]['occupation'].toString()),
// // "religion": encryptionHelper
// // .encryptData(drivers[i]['religion'].toString()),
// // "site": encryptionHelper
// // .encryptData(drivers[i]['site'].toString()),
// // "education": encryptionHelper
// // .encryptData(drivers[i]['education'].toString()),
// // "accountBank": encryptionHelper
// // .encryptData(drivers[i]['accountBank'].toString()),
// // "employmentType": encryptionHelper
// // .encryptData(drivers[i]['employmentType'].toString()),
// // "maritalStatus": (drivers[i]['maritalStatus'].toString()),
// // "fullNameMaritial": encryptionHelper.encryptData(
// // drivers[i]['fullNameMaritial'].toString()),
// 'id': drivers[i]['id'].toString()
// };
// // print(drivers[i]['idn']);
// // if (drivers[i]['id'].toString() !=
// // '01002165502a9sHC1tbrUrUw') {
// var result = await CRUD().post(
// link:
// 'https://server.sefer.live/sefer.click/sefer/auth/captin/updateDriverSecure.php',
// payload: payload);
// if (result != 'failure') {
// print(result);
// } else {
// print('failure');
// }
// // Future.delayed(Duration(microseconds: 200));
// // }
// }
// MyDialog().getDialog('title', 'midTitle', () {
// Get.back();
// });
// },
// icon: const Icon(
// FontAwesome5.grin_tears,
// size: 29,
// color: AppColor.blueColor,
// ),
// ),
],
);
}),
],
isleading: false,
);
}
}

View File

@@ -0,0 +1,987 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/controller/functions/audio_controller.dart';
import 'package:sefer_driver/main.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../../../constant/colors.dart';
import '../../../../constant/links.dart';
import '../../../../constant/style.dart';
import '../../../../controller/functions/gemeni.dart';
import '../../../../controller/functions/package_info.dart';
import '../../../../controller/functions/tts.dart';
import '../../../../print.dart';
import '../../../widgets/elevated_btn.dart';
import '../../../widgets/my_circular_indicator_timer.dart';
import '../../../widgets/my_scafold.dart';
import '../../../widgets/mydialoug.dart';
// --- اقتراحات الألوان الجديدة ---
// يمكنك تعريف هذه الألوان في ملف AppColor.dart الخاص بك
class NewAppColor {
static const Color primaryColor = Color(0xFF0D47A1); // أزرق داكن
static const Color accentColor = Color(0xFF1976D2); // أزرق أفتح
static const Color backgroundColor = Color(0xFFF5F7FA); // رمادي فاتح للخلفية
static const Color cardColor = Colors.white;
static const Color textColor = Color(0xFF333333); // أسود ناعم للنصوص
static const Color subTextColor = Color(0xFF757575); // رمادي للنصوص الفرعية
static const Color successColor = Color(0xFF2E7D32); // أخضر للنجاح
static const Color errorColor = Color(0xFFC62828); // أحمر للخطأ
static const Color borderColor = Color(0xFFE0E0E0); // لون الحدود
}
// --- اقتراحات للخطوط ---
// يمكنك استخدام حزمة google_fonts وتعيين الخط 'Cairo' أو 'Tajawal' للتطبيق
class NewAppStyle {
static TextStyle get headlineStyle {
return const TextStyle(
fontFamily: 'Cairo', // اسم الخط المقترح
fontSize: 18,
fontWeight: FontWeight.bold,
color: NewAppColor.primaryColor,
);
}
static TextStyle get titleStyle {
return const TextStyle(
fontFamily: 'Cairo',
fontSize: 16,
fontWeight: FontWeight.bold,
color: NewAppColor.textColor,
);
}
static TextStyle get bodyStyle {
return const TextStyle(
fontFamily: 'Cairo',
fontSize: 14,
color: NewAppColor.subTextColor,
height: 1.5,
);
}
static TextStyle get valueStyle {
return const TextStyle(
fontFamily: 'Cairo',
fontSize: 15,
fontWeight: FontWeight.w600,
color: NewAppColor.textColor,
);
}
}
class SyrianCardAI extends StatelessWidget {
SyrianCardAI({super.key});
final TextToSpeechController textToSpeechController =
Get.put(TextToSpeechController());
@override
Widget build(BuildContext context) {
Get.put(AI());
WidgetsBinding.instance.addPostFrameCallback((_) {
checkForUpdate(context);
});
return MyScafolld(
// تم تغيير لون الخلفية للتصميم الجديد
// backgroundColor: NewAppColor.backgroundColor,
title: "Approve Driver Documents".tr,
action: GetBuilder<AI>(builder: (cont) {
return IconButton(
onPressed: () {
cont.isLoading = false;
cont.update();
},
icon: const Icon(Icons.refresh, color: NewAppColor.primaryColor),
);
}),
body: [
GetBuilder<AI>(builder: (controller) {
return controller.isLoading
? MyCircularProgressIndicatorWithTimer(
isLoading: controller.isLoading,
)
: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12.0, vertical: 8.0),
child: Column(
children: [
// --- زر "التالي" بتصميم جديد ---
if (controller.licenceFrontSy.isNotEmpty &&
controller.licenceBackSy.isNotEmpty &&
(controller.idFrontSy.isNotEmpty) &&
(controller.idBackSy.isNotEmpty) &&
controller.vehicleFrontSy.isNotEmpty &&
controller.vehicleBackSy.isNotEmpty)
Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: MyElevatedButton(
title: 'التالي'.tr,
// استخدام اللون الجديد للنجاح
kolor: NewAppColor.successColor,
onPressed: () {
controller.addDriverAndCarEgypt();
},
),
),
Expanded(
child: ListView(
children: [
// --- فيديو الشرح بتصميم جديد ---
VideoButton(),
const SizedBox(height: 16),
egyptDriverLicense(),
const SizedBox(height: 16),
syriaDriverLicenseBack(),
const SizedBox(height: 16),
syriaVehicleCardFront(),
const SizedBox(height: 16),
syriaVehicleCardBack(),
const SizedBox(height: 16),
syriaIdCardFront(),
const SizedBox(height: 16),
syriaDriverIDBack(),
const SizedBox(height: 16),
// egyptCriminalRecord(),
// const SizedBox(height: 24),
],
),
),
],
),
);
}),
// --- شاشة الموافقة بتصميم جديد ---
Positioned(
top: 0,
bottom: 0,
right: 0,
left: 0,
child: GetBuilder<AI>(builder: (controller) {
return controller.approved == false
// --- إضافة خلفية معتمة ---
? Container(
color: Colors.black.withOpacity(0.6),
child: Center(
child: Container(
margin: const EdgeInsets.all(24),
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 15,
spreadRadius: 5,
)
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Approve Driver Documents".tr,
style: NewAppStyle.headlineStyle,
),
const SizedBox(height: 16),
Text(
"To become a ride-sharing driver on the Intaleq app, you need to upload your driver's license, ID document, and car registration document. Our AI system will instantly review and verify their authenticity in just 2-3 minutes. If your documents are approved, you can start working as a driver on the Intaleq app. Please note, submitting fraudulent documents is a serious offense and may result in immediate termination and legal consequences."
.tr,
style: NewAppStyle.bodyStyle,
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
// --- زر الاستماع بتصميم جديد ---
TextButton.icon(
style: TextButton.styleFrom(
foregroundColor: NewAppColor.accentColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 10),
),
onPressed: () async {
controller.startTimer();
if (box.read(BoxName.lang) == 'ar') {
await Get.put(AudioController())
.playAudio1('assets/aggrement.wav');
} else {
await textToSpeechController.speakText(
'To become a ride-sharing driver on the Intaleq app...'
.tr);
}
},
icon: const Icon(Icons.volume_up_outlined,
size: 30),
label: Text('اضغط للاستماع'.tr,
style: AppStyle.title.copyWith(
color: NewAppColor.accentColor)),
),
const SizedBox(height: 24),
// --- أزرار الموافقة والرفض بتصميم جديد ---
controller.isTimerComplete
? Row(
children: [
Expanded(
child: MyElevatedButton(
title: 'إلغاء'.tr,
kolor: NewAppColor.errorColor,
onPressed: () {
MyDialog().getDialog(
'سيتم إلغاء التسجيل'.tr, '',
() async {
Get.back();
Get.back();
});
}),
),
const SizedBox(width: 16),
Expanded(
child: MyElevatedButton(
title: 'أوافق'.tr,
kolor: NewAppColor.successColor,
onPressed: () {
controller.setApproved();
}),
),
],
)
: Column(
children: [
CircularProgressIndicator(
value: controller.progressValue,
color: NewAppColor.primaryColor,
),
const SizedBox(height: 8),
Text(
'${'الوقت المتبقي'.tr}: ${controller.remainingSeconds} ${"ثانية".tr}',
style: NewAppStyle.bodyStyle,
),
],
),
],
),
),
),
)
: const SizedBox();
}),
)
],
isleading: true,
);
}
// --- واجهة عرض بيانات الوثيقة ---
Widget _buildDocumentDataCard({
required String title,
required VoidCallback onRefresh,
required List<Widget> children,
}) {
return Card(
elevation: 4.0,
color: NewAppColor.cardColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0),
side: BorderSide(color: NewAppColor.borderColor, width: 1),
),
shadowColor: NewAppColor.primaryColor.withOpacity(0.1),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(title, style: NewAppStyle.headlineStyle),
IconButton(
onPressed: onRefresh,
icon:
const Icon(Icons.refresh, color: NewAppColor.accentColor),
),
],
),
const Divider(
height: 24, thickness: 1, color: NewAppColor.borderColor),
...children,
],
),
),
);
}
// --- واجهة التقاط صورة الوثيقة ---
Widget _buildCaptureCard({
required String title,
required String imagePath,
required VoidCallback onTap,
}) {
return GestureDetector(
onTap: onTap,
child: Card(
clipBehavior: Clip.antiAlias,
elevation: 2.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Image.asset(
imagePath,
height: Get.height * .20,
fit: BoxFit.cover,
// --- في حال لم يتم العثور على الصورة ---
errorBuilder: (context, error, stackTrace) {
return Container(
height: Get.height * .20,
color: NewAppColor.borderColor,
child: const Icon(
Icons.camera_alt_outlined,
size: 50,
color: NewAppColor.subTextColor,
),
);
},
),
Container(
padding: const EdgeInsets.all(16.0),
color: NewAppColor.cardColor,
child: Text(
title,
style: AppStyle.title,
textAlign: TextAlign.center,
),
),
],
),
),
);
}
// --- ويدجت لعرض معلومة (سطر) ---
Widget _infoRow(String label, String? value, {Color? valueColor}) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('$label: ', style: NewAppStyle.bodyStyle),
Expanded(
child: Text(
value ?? 'غير متوفر',
style: NewAppStyle.valueStyle.copyWith(color: valueColor),
),
),
],
),
);
}
GetBuilder<AI> egyptDriverLicense() {
return GetBuilder<AI>(
builder: (ai) {
if (ai.licenceFrontSy.isNotEmpty) {
final data = ai.licenceFrontSy;
DateTime? expiryDateTime;
bool isExpired = false;
if (data['expiry_date'] != null) {
expiryDateTime = DateTime.tryParse(data['expiry_date']);
isExpired = expiryDateTime != null &&
expiryDateTime.isBefore(DateTime.now());
}
// بطاقة «رخصة القيادة الوجه الأمامي» بتنسيق مضغوط وأيقونات
return Card(
elevation: 2,
color: NewAppColor.cardColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
side: BorderSide(color: NewAppColor.borderColor, width: .8),
),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// العنوان + زر التحديث
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('رخصة القيادة الوجه الأمامي'.tr,
style: NewAppStyle.headlineStyle),
IconButton(
icon: const Icon(Icons.refresh,
size: 20, color: NewAppColor.accentColor),
splashRadius: 18,
onPressed: () async => await ai
.pickAndSendImage('driving_license_sy_front'),
),
],
),
const SizedBox(height: 8),
// سطر الاسم الكامل
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (data['name_arabic'] != null)
_iconInfo(Icons.person, data['name_arabic']!),
if (data['birth_place'] != null)
_iconInfo(Icons.location_city, data['birth_place']!),
],
),
const SizedBox(height: 8),
// بقية الحقول داخل Wrap لتقليل الطول
Wrap(
spacing: 12,
runSpacing: 8,
children: [
if (data['national_number'] != null)
_iconInfo(Icons.badge, data['national_number']!),
if (data['civil_registry'] != null)
_iconInfo(Icons.location_on, data['civil_registry']!),
if (data['blood_type'] != null)
_iconInfo(Icons.water_drop, data['blood_type']!),
if (data['birth_year'] != null)
_iconInfo(Icons.calendar_today, data['birth_year']!),
],
),
],
),
),
);
}
return _buildCaptureCard(
title: 'التقط صورة لرخصة القيادة'.tr,
imagePath: 'assets/images/1.png',
onTap: () async {
await ai.pickAndSendImage('driving_license_sy_front');
},
);
},
);
}
GetBuilder<AI> syriaDriverLicenseBack() {
return GetBuilder<AI>(
builder: (ai) {
if (ai.licenceBackSy.isNotEmpty) {
final data = ai.licenceBackSy;
// صلاحية الرخصة
final DateTime? expDate =
DateTime.tryParse(data['expiry_date'] ?? '');
final bool expired =
expDate != null && expDate.isBefore(DateTime.now());
final Color expColor = expired
? NewAppColor.errorColor // أحمر إن انتهت
: NewAppColor.successColor; // أخضر إن صالحة
return Card(
elevation: 2,
color: NewAppColor.cardColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
side: BorderSide(color: NewAppColor.borderColor, width: .8),
),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// عنوان البطاقة + زر الإنعاش
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('رخصة القيادة الوجه الخلفي'.tr,
style: NewAppStyle.headlineStyle),
IconButton(
splashRadius: 18,
icon: const Icon(Icons.refresh,
size: 20, color: NewAppColor.accentColor),
onPressed: () async => await ai
.pickAndSendImage('driving_license_sy_back'),
),
],
),
const SizedBox(height: 8),
// صفّ أول (الفئة + الرقم)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (data['license_category'] != null)
_iconInfo(
Icons.star, data['license_category']!), // D1 / D2…
if (data['license_number'] != null)
_iconInfo(
Icons.confirmation_number, data['license_number']!),
],
),
const SizedBox(height: 8),
// صفّ ثانٍ (التواريخ) مع تلوين تاريخ الصلاحية
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (data['issue_date'] != null)
_iconInfo(Icons.event, data['issue_date']!),
if (data['expiry_date'] != null)
_iconInfo(Icons.event_busy, data['expiry_date']!,
valueColor: expColor),
],
),
],
),
),
);
}
// بطاقة الالتقاط الافتراضية
return _buildCaptureCard(
title: 'التقط صورة الوجه الخلفي للرخصة'.tr,
imagePath: 'assets/images/5.png',
onTap: () async =>
await ai.pickAndSendImage('driving_license_sy_back'),
);
},
);
}
GetBuilder<AI> syriaDriverIDBack() {
return GetBuilder<AI>(
builder: (ai) {
// استلمنا الحقول الأربعة فقط (governorate-address-gender-issue_date)
if (ai.idBackSy.isNotEmpty) {
final data = ai.idBackSy;
return Card(
elevation: 2,
color: NewAppColor.cardColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
side: BorderSide(color: NewAppColor.borderColor, width: .8),
),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// العنوان + زر التحديث
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('بطاقة الهوية الوجه الخلفي'.tr,
style: NewAppStyle.headlineStyle),
IconButton(
splashRadius: 18,
icon: const Icon(Icons.refresh,
size: 20, color: NewAppColor.accentColor),
onPressed: () async =>
await ai.pickAndSendImage('id_back_sy'),
),
],
),
const SizedBox(height: 8),
// المحافظة + العنوان
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (data['governorate'] != null)
_iconInfo(Icons.location_city, data['governorate']!),
if (data['address'] != null)
_iconInfo(Icons.home, data['address']!),
],
),
const SizedBox(height: 8),
// الجنس + تاريخ الإصدار
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (data['gender'] != null)
_iconInfo(Icons.person, data['gender']!),
if (data['issue_date'] != null)
_iconInfo(Icons.event, data['issue_date']!),
],
),
],
),
),
);
}
// البطاقة الافتراضية لالتقاط الصورة
return _buildCaptureCard(
title: 'التقط صورة للوجه الخلفي للهوية'.tr,
imagePath: 'assets/images/4.png',
onTap: () async => await ai.pickAndSendImage('id_back_sy'),
);
},
);
}
/// عنصر (أيقونة + قيمة) مع لون نص مخصّص عند الحاجة
Widget _iconInfo(IconData icon, String value, {Color? valueColor}) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, size: 18, color: NewAppColor.accentColor),
const SizedBox(width: 4),
Flexible(
child: Text(
value.tr,
style: NewAppStyle.bodyStyle.copyWith(color: valueColor),
overflow: TextOverflow.ellipsis,
),
),
],
);
}
GetBuilder<AI> syriaIdCardFront() {
// أبقِ الاسم القديم إذا أردت
return GetBuilder<AI>(
builder: (ai) {
if (ai.idFrontSy.isNotEmpty) {
// غيّر المفتاح حسب متغيرك
final data = ai.idFrontSy;
return Card(
elevation: 2,
color: NewAppColor.cardColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
side: BorderSide(color: NewAppColor.borderColor, width: .8),
),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// العنوان + زر التحديث
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('بطاقة الهوية الوجه الأمامي'.tr,
style: NewAppStyle.headlineStyle),
IconButton(
splashRadius: 18,
icon: const Icon(Icons.refresh,
size: 20, color: NewAppColor.accentColor),
onPressed: () async => await ai.pickAndSendImage(
'id_front_sy', // أو id_front حسب تسمية end-point
),
),
],
),
const SizedBox(height: 8),
// سطر الاسم الكامل
if (data['full_name'] != null)
_iconInfo(Icons.person, data['full_name']!),
const SizedBox(height: 8),
// صفّ (الرقم الوطني + تاريخ الميلاد)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (data['national_number'] != null)
_iconInfo(Icons.badge, data['national_number']!),
if (data['dob'] != null)
_iconInfo(Icons.cake, data['dob']!),
],
),
const SizedBox(height: 8),
// العنوان كامل بمفرده
if (data['address'] != null)
_iconInfo(Icons.home, data['address']!),
],
),
),
);
}
// كارد الالتقاط الافتراضية
return _buildCaptureCard(
title: 'التقط صورة للوجه الأمامي للهوية'.tr,
imagePath: 'assets/images/2.png',
onTap: () async => await ai.pickAndSendImage('id_front_sy'),
);
},
);
}
GetBuilder<AI> syriaVehicleCardFront() {
// يمكنك إبقاء الاسم القديم إن شئت
return GetBuilder<AI>(
builder: (ai) {
if (ai.vehicleFrontSy.isNotEmpty) {
final data = ai.vehicleFrontSy;
// تاريخ الفحص القادم للفحص الدوري (inspection_date)
final DateTime? nextCheck =
DateTime.tryParse(data['inspection_date'] ?? '');
final bool overdue =
nextCheck != null && nextCheck.isBefore(DateTime.now());
final Color checkColor =
overdue ? NewAppColor.errorColor : NewAppColor.successColor;
return Card(
elevation: 2,
color: NewAppColor.cardColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
side: BorderSide(color: NewAppColor.borderColor, width: .8),
),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// العنوان + تحديث
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('رخصة المركبة الوجه الأمامي'.tr,
style: NewAppStyle.headlineStyle),
IconButton(
splashRadius: 18,
icon: const Icon(Icons.refresh,
size: 20, color: NewAppColor.accentColor),
onPressed: () async => await ai.pickAndSendImage(
'vehicle_license_sy_front',
),
),
],
),
const SizedBox(height: 8),
// الصف الأوّل (لوحة + مالك)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (data['car_plate'] != null)
_iconInfo(Icons.directions_car, data['car_plate']!),
if (data['owner'] != null)
_iconInfo(Icons.person, data['owner']!),
],
),
const SizedBox(height: 8),
// الصف الثاني (VIN + اللون)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (data['vin'] != null)
_iconInfo(Icons.confirmation_num, data['vin']!),
if (data['color'] != null)
_iconInfo(Icons.palette, data['color']!),
],
),
const SizedBox(height: 8),
// الصف الثالث (تاريخ المنح + الفحص القادم)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (data['issue_date'] != null)
_iconInfo(Icons.event, data['issue_date']!),
if (data['inspection_date'] != null)
_iconInfo(
Icons.event_available, data['inspection_date']!,
valueColor: checkColor),
],
),
],
),
),
);
}
// لو لم تُرفع صورة بعد
return _buildCaptureCard(
title: 'التقط صورة لوجه رخصة المركبة'.tr,
imagePath: 'assets/images/6.png',
onTap: () async =>
await ai.pickAndSendImage('vehicle_license_sy_front'),
);
},
);
}
GetBuilder<AI> syriaVehicleCardBack() {
// أبقِ الاسم القديم إن أردت
return GetBuilder<AI>(
builder: (ai) {
if (ai.vehicleBackSy.isNotEmpty) {
final data = ai.vehicleBackSy;
return Card(
elevation: 2,
color: NewAppColor.cardColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
side: BorderSide(color: NewAppColor.borderColor, width: .8),
),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// العنوان + زر تحديث
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('رخصة المركبة الوجه الخلفي'.tr,
style: NewAppStyle.headlineStyle),
IconButton(
splashRadius: 18,
icon: const Icon(Icons.refresh,
size: 20, color: NewAppColor.accentColor),
onPressed: () async => await ai.pickAndSendImage(
'vehicle_license_sy_back',
),
),
],
),
const SizedBox(height: 8),
// صفّ (الشركة + الطراز)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (data['make'] != null)
_iconInfo(Icons.factory, data['make']!),
if (data['model'] != null)
_iconInfo(Icons.directions_car_filled, data['model']!),
],
),
const SizedBox(height: 8),
// صفّ (سنة الصنع + اللون)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (data['year'] != null)
_iconInfo(Icons.calendar_today, data['year']!),
if (data['fuel'] != null)
_iconInfo(Icons.local_gas_station, data['fuel']!),
],
),
const SizedBox(height: 8),
// رقم الهيكل بمفرده (قد يكون طويلًا)
if (data['chassis'] != null)
_iconInfo(Icons.confirmation_num, data['chassis']!),
],
),
),
);
}
// بطاقة الالتقاط الافتراضية
return _buildCaptureCard(
title: 'التقط صورة لخلفية رخصة المركبة'.tr,
imagePath: 'assets/images/3.png',
onTap: () async =>
await ai.pickAndSendImage('vehicle_license_sy_back'),
);
},
);
}
GetBuilder<AI> egyptCriminalRecord() {
return GetBuilder<AI>(
builder: (ai) {
if (ai.responseCriminalRecordEgypt.isNotEmpty) {
return _buildDocumentDataCard(
title: 'صحيفة الحالة الجنائية'.tr,
onRefresh: () async {
await ai.allMethodForAI(
(ai.prompts[5]['prompt'].toString()),
AppLink.uploadEgypt,
'criminalRecord',
);
},
children: [
_infoRow('نتيجة الفحص'.tr,
ai.responseCriminalRecordEgypt['InspectionResult']),
_infoRow(
'الاسم الكامل'.tr,
ai.responseCriminalRecordEgypt['FullName'],
valueColor: (ai.responseCriminalRecordEgypt['FullName']) ==
(ai.responseIdEgyptDriverLicense['name_arabic'])
? NewAppColor.successColor
: NewAppColor.errorColor,
),
_infoRow('الرقم القومي'.tr,
ai.responseCriminalRecordEgypt['NationalID']),
],
);
}
return _buildCaptureCard(
title: 'التقط صورة لصحيفة الحالة الجنائية'.tr,
imagePath: 'assets/images/6.png',
onTap: () async {
await ai.allMethodForAI(
(ai.prompts[5]['prompt'].toString()),
AppLink.uploadEgypt,
'criminalRecord',
);
},
);
},
);
}
}
// --- واجهة عرض الأقسام غير المصرية (بدون تغيير) ---
// --- زر الفيديو بتصميم جديد ---
class VideoButton extends StatelessWidget {
final String videoUrl =
"https://youtube.com/shorts/fC0RmYH5B_0?feature=share";
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: InkWell(
onTap: () async {
if (await canLaunchUrl(Uri.parse(videoUrl))) {
await launchUrl(Uri.parse(videoUrl));
} else {
Get.snackbar('خطأ', 'لا يمكن فتح الفيديو');
}
},
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.play_circle_outline,
color: NewAppColor.accentColor, size: 28),
const SizedBox(width: 12),
Text(
"شاهد فيديو الشرح".tr,
style: AppStyle.title.copyWith(color: NewAppColor.accentColor),
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,100 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../../controller/functions/tts.dart';
import '../../../controller/home/captin/contact_us_controller.dart';
import '../../widgets/my_scafold.dart';
class ContactUsPage extends StatelessWidget {
ContactUsPage({super.key});
@override
Widget build(BuildContext context) {
Get.put(ContactUsController());
return GetBuilder<ContactUsController>(builder: (controller) {
return MyScafolld(
title: "Contact Us".tr,
body: [
Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
// crossAxisAlignment: CrossAxisAlignment.center,
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
decoration: AppStyle.boxDecoration1,
child: Column(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(15),
child: Image.asset('assets/images/logo.gif')),
IconButton(
onPressed: () async {
Get.put(TextToSpeechController()).speakText(
'Tripz is the safest ride-sharing app that introduces many features for both captains and passengers. We offer the lowest commission rate of just 8%, ensuring you get the best value for your rides. Our app includes insurance for the best captains, regular maintenance of cars with top engineers, and on-road services to ensure a respectful and high-quality experience for all users.'
.tr);
},
icon: const Icon(Icons.headphones),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Tripz is the safest ride-sharing app that introduces many features for both captains and passengers. We offer the lowest commission rate of just 8%, ensuring you get the best value for your rides. Our app includes insurance for the best captains, regular maintenance of cars with top engineers, and on-road services to ensure a respectful and high-quality experience for all users.'
.tr,
style: AppStyle.title,
textAlign: TextAlign.center,
),
),
],
),
),
const SizedBox(
height: 30,
),
Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"You can contact us during working hours from 12:00 - 19:00."
.tr,
style: AppStyle.title,
textAlign: TextAlign.center,
),
),
),
InkWell(
onTap: () => controller.showContactDialog(context),
child: const Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Icon(
Icons.phone,
color: AppColor.blueColor,
),
Icon(
FontAwesome.whatsapp,
color: AppColor.greenColor,
),
Icon(
Icons.email,
color: AppColor.redColor,
),
],
),
),
const SizedBox(
height: 30,
)
],
),
)
],
isleading: true);
});
}
}

View File

@@ -0,0 +1,211 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
import '../../../constant/links.dart';
import '../../../constant/style.dart';
import '../../../controller/functions/encrypt_decrypt.dart';
import '../../../controller/functions/gemeni.dart';
import '../../../controller/functions/tts.dart';
import '../../../main.dart';
import '../../widgets/elevated_btn.dart';
import '../../widgets/my_scafold.dart';
class CriminalDocumemtPage extends StatelessWidget {
const CriminalDocumemtPage({super.key});
@override
Widget build(BuildContext context) {
Get.put(AI());
return MyScafolld(
title: "Criminal Document".tr,
isleading: false,
body: [
GetBuilder<AI>(builder: (controller) {
return Column(
children: [
Container(
decoration: AppStyle.boxDecoration,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text('You have upload Criminal documents'.tr),
)),
egyptCriminalRecord(),
controller.responseCriminalRecordEgypt.isNotEmpty
? MyElevatedButton(
title: 'Next'.tr,
onPressed: () async {
if ((controller
.responseCriminalRecordEgypt['FullName']) !=
box.read(BoxName.nameArabic)) //todo get from server
{
Get.defaultDialog(
barrierDismissible: false,
title: 'Criminal Record Mismatch',
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.warning,
size: 48, color: Colors.red),
const SizedBox(height: 16),
Text(
'The full name on your criminal record does not match the one on your drivers license. Please verify and provide the correct documents.'
.tr,
textAlign: TextAlign.center,
style: AppStyle.title,
),
const SizedBox(height: 16),
IconButton(
onPressed: () async {
await Get.find<TextToSpeechController>()
.speakText(
'The full name on your criminal record does not match the one on your drivers license. Please verify and provide the correct documents.'
.tr,
);
},
icon: const Icon(Icons.volume_up),
),
],
),
actions: [
TextButton(
onPressed: () {
Get.back();
},
child: const Text('OK'),
),
],
);
} else {
await controller.addCriminalDocuments();
}
})
: const SizedBox(),
],
);
})
],
);
}
GetBuilder<AI> egyptCriminalRecord() {
return GetBuilder<AI>(
builder: (ai) {
if (ai.responseCriminalRecordEgypt.isNotEmpty) {
return Card(
elevation: 4.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Criminal Record'.tr, style: AppStyle.headTitle2),
IconButton(
onPressed: () async {
await ai.allMethodForAI(
"""
Write a JSON object from the following information extracted from the provided Arabic text:
{
"InspectionResult": "",
"NationalID": "",
"FullName": "",
"IssueDate": "" // Format: YYYY-MM-DD
}
Important notes:
1. For the IssueDate, ensure the date is in YYYY-MM-DD format using Latin numerals (0-9).
2. Add appropriate spaces in all text fields to ensure readability.
3. If any information is missing, leave the corresponding field as an empty string.
4. Ensure all text is properly formatted and spaces are used correctly.
5. Convert any Arabic numerals to Latin numerals (0-9) where applicable.
Please fill in the JSON object with the extracted information, following these guidelines.
""",
AppLink.uploadEgypt,
'criminalRecord',
);
},
icon: const Icon(Icons.refresh),
),
],
),
const SizedBox(height: 8.0),
const Divider(color: AppColor.accentColor),
const SizedBox(height: 8.0),
Text(
'${'InspectionResult'.tr}: ${(ai.responseCriminalRecordEgypt['InspectionResult'])}'),
const SizedBox(height: 8.0),
Text(
'${'FullName'.tr}: ${(ai.responseCriminalRecordEgypt['FullName'])}',
style: AppStyle.title.copyWith(
color: (ai.responseCriminalRecordEgypt['FullName']) ==
(ai.responseIdEgyptDriverLicense['name_arabic'])
? AppColor.greenColor
: AppColor.redColor),
),
const SizedBox(height: 8.0),
Text(
'${'NationalID'.tr}: ${(ai.responseCriminalRecordEgypt['NationalID'])}'),
const SizedBox(height: 8.0),
Text(
'${'IssueDate'.tr}: ${ai.responseCriminalRecordEgypt['IssueDate']}'),
],
),
),
);
}
return Card(
child: InkWell(
onTap: () async {
await ai.allMethodForAI(
"""
Write a JSON object from the following information extracted from the provided Arabic text:
{
"InspectionResult": "",
"NationalID": "",
"FullName": "",
"IssueDate": "" // Format: YYYY-MM-DD
}
Important notes:
1. For the IssueDate, ensure the date is in YYYY-MM-DD format using Latin numerals (0-9).
2. Add appropriate spaces in all text fields to ensure readability.
3. If any information is missing, leave the corresponding field as an empty string.
4. Ensure all text is properly formatted and spaces are used correctly.
5. Convert any Arabic numerals to Latin numerals (0-9) where applicable.
Please fill in the JSON object with the extracted information, following these guidelines.
""",
AppLink.uploadEgypt,
'criminalRecord',
);
},
child: Column(
children: [
Image.asset(
'assets/images/6.png',
height: Get.height * .25,
width: double.maxFinite,
fit: BoxFit.fitHeight,
),
Text(
'Capture an Image of Your Criminal Record'.tr,
style: AppStyle.title,
),
],
),
),
);
},
);
}
}

View File

@@ -0,0 +1,110 @@
import 'dart:convert';
import 'package:get/get.dart';
import '../../../constant/box_name.dart';
import '../../../constant/links.dart';
import '../../../controller/functions/crud.dart';
import '../../../main.dart';
import '../../widgets/error_snakbar.dart';
class DriverCarController extends GetxController {
bool isLoading = false;
List cars = [];
// int? carId;
fetchCatrsForDrivers() async {
isLoading = true;
update();
var res = await CRUD().get(link: AppLink.getNewCarsDrivers, payload: {
"driverID": box.read(BoxName.driverID).toString(),
});
if (res != 'failure') {
var d = jsonDecode(res)['message'];
cars = d;
// carId = cars.isEmpty ? 1 : cars.length + 1;
}
isLoading = false;
update();
}
addCarsForDrivers(
String vin,
String carPlate,
String make,
String model,
String year,
String expirationDate,
String color,
String colorHex,
String address,
String owner,
String registrationDate,
String displacement,
String fuel) async {
var res = await CRUD().post(
link: AppLink.addRegisrationCar,
payload: {
"driverID": box.read(BoxName.driverID).toString(),
"vin": vin ?? 'unknown',
"car_plate": carPlate.toString(),
"make": make ?? 'unknown',
"model": model ?? 'unknown',
"year": year ?? 'unknown',
"expiration_date": expirationDate ?? 'unknown',
"color": color ?? 'unknown',
"owner": owner ?? 'unknown',
"color_hex": colorHex ?? '#000000',
"address": address ?? 'unknown',
"displacement": displacement ?? 'unknown',
"fuel": fuel ?? 'unknown',
"registration_date": registrationDate ?? 'unknown',
},
);
if (res != 'failure') {
mySnackbarSuccess('');
fetchCatrsForDrivers();
} else {
mySnackeBarError('');
}
}
// update carRegistration only and insert on it without tow column
Future<void> updateCarRegistration(String id, String driverID) async {
final body = {
'id': id,
'driverID': driverID,
};
// remove default before update
var response = await CRUD().post(
link: AppLink.makeDefaultCar,
payload: body,
);
if (response != 'failure') {
mySnackbarSuccess('Updated'.tr);
} else {
mySnackeBarError('Not updated'.tr);
}
}
//todo need review
removeCar(String carId) async {
isLoading = true;
update();
var res = await CRUD().post(link: AppLink.deleteNewCarsDrivers, payload: {
"id": carId.toString(),
});
if (res != 'failure') {
mySnackbarSuccess('deleted'.tr);
}
isLoading = false;
update();
}
@override
void onInit() {
fetchCatrsForDrivers();
super.onInit();
}
}

View File

View File

@@ -0,0 +1,640 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
import '../../../controller/auth/captin/invit_controller.dart';
import '../../../controller/functions/encrypt_decrypt.dart';
import '../../../main.dart';
class InviteScreen extends StatelessWidget {
final InviteController controller = Get.put(InviteController());
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: CupertinoColors.systemBackground,
appBar: AppBar(
backgroundColor: CupertinoColors.systemBackground,
elevation: 0,
title: Text(
'Invite'.tr,
style: const TextStyle(color: CupertinoColors.label),
),
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios, color: AppColor.blueColor),
onPressed: () => Get.back(),
),
),
body: SafeArea(
child: GetBuilder<InviteController>(
builder: (controller) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(8),
),
child: SegmentedButton<int>(
style: ButtonStyle(
backgroundColor: WidgetStateProperty.resolveWith<Color>(
(states) => states.contains(WidgetState.selected)
? CupertinoColors.white
: Colors.transparent,
),
foregroundColor: WidgetStateProperty.resolveWith<Color>(
(states) => states.contains(WidgetState.selected)
? AppColor.blueColor
: CupertinoColors.label,
),
),
segments: [
ButtonSegment(
value: 0,
label: Padding(
padding: const EdgeInsets.all(8.0),
child: Text('Drivers'.tr),
),
),
ButtonSegment(
value: 1,
label: Padding(
padding: const EdgeInsets.all(8.0),
child: Text('Passengers'.tr),
),
),
],
selected: {controller.selectedTab},
onSelectionChanged: (Set<int> newSelection) {
controller.updateSelectedTab(newSelection.first);
},
),
),
),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: controller.selectedTab == 0
? _buildDriverTab(context)
: _buildPassengerTab(context),
),
),
],
);
},
),
),
);
}
Widget _buildDriverTab(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20),
Text(
"Invite another driver and both get a gift after he completes 100 trips!"
.tr,
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
color: CupertinoColors.label,
),
),
const SizedBox(height: 20),
_buildPhoneInput(),
const SizedBox(height: 20),
_buildActionButtons(),
const SizedBox(height: 20),
_buildInvitationsList(context),
],
);
}
Widget _buildPassengerTab(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(12),
),
child: Column(
children: [
Text(
"Share this code with passengers and earn rewards when they use it!"
.tr,
textAlign: TextAlign.center,
style: const TextStyle(
color: CupertinoColors.secondaryLabel,
fontSize: 13,
),
),
],
),
),
const SizedBox(height: 20),
_buildPhoneInput(),
const SizedBox(height: 20),
_buildActionButtonsPassengers(),
const SizedBox(height: 20),
const SizedBox(height: 20),
_buildInvitationsListPassengers(context),
],
);
}
Widget _buildPhoneInput() {
return Container(
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Expanded(
child: CupertinoTextField.borderless(
controller: controller.invitePhoneController,
placeholder: 'Enter phone'.tr,
padding: const EdgeInsets.all(12),
keyboardType: TextInputType.phone,
),
),
CupertinoButton(
child: const Icon(CupertinoIcons.person_badge_plus,
color: AppColor.blueColor),
onPressed: () async {
await controller.pickContacts();
if (controller.contacts.isNotEmpty) {
if (box.read(BoxName.isSavedPhones) == null) {
// controller.savePhoneToServer();
box.write(BoxName.isSavedPhones, true);
}
_showContactsDialog(Get.context!);
}
},
),
],
),
);
}
Widget _buildActionButtons() {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 16.0),
child: Row(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 3),
),
],
),
child: CupertinoButton(
color: AppColor.blueColor,
borderRadius: BorderRadius.circular(10),
padding: const EdgeInsets.symmetric(vertical: 14),
onPressed: controller.sendInvite,
child: Text(
'Send Invite'.tr,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: CupertinoColors.white,
),
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 3),
),
],
),
child: CupertinoButton(
color: AppColor.blueColor,
borderRadius: BorderRadius.circular(10),
padding: const EdgeInsets.symmetric(vertical: 14),
child: Text(
'Show Invitations'.tr,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: CupertinoColors.white,
),
),
onPressed: () async {
controller.fetchDriverStats();
},
),
),
),
],
),
);
}
Widget _buildActionButtonsPassengers() {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 16.0),
child: Row(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 3),
),
],
),
child: CupertinoButton(
color: AppColor.blueColor,
borderRadius: BorderRadius.circular(10),
padding: const EdgeInsets.symmetric(vertical: 14),
onPressed: controller.sendInviteToPassenger,
child: Text(
'Send Invite'.tr,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: CupertinoColors.white,
),
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 3),
),
],
),
child: CupertinoButton(
color: AppColor.blueColor,
borderRadius: BorderRadius.circular(10),
padding: const EdgeInsets.symmetric(vertical: 14),
child: Text(
'Show Invitations'.tr,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: CupertinoColors.white,
),
),
onPressed: () async {
controller.fetchDriverStatsPassengers();
},
),
),
),
],
),
);
}
Widget _buildInvitationsList(BuildContext context) {
return SizedBox(
height: Get.height * .4,
child: controller.driverInvitationData.isEmpty
? Center(
child: Text(
"No invitation found yet!".tr,
style: const TextStyle(
color: CupertinoColors.secondaryLabel,
fontSize: 17,
),
),
)
: ListView.builder(
itemCount: controller.driverInvitationData.length,
itemBuilder: (context, index) {
return _buildInvitationItem(context, index);
},
),
);
}
Widget _buildInvitationsListPassengers(BuildContext context) {
return SizedBox(
height: Get.height * .4,
child: controller.driverInvitationDataToPassengers.isEmpty
? Center(
child: Text(
"No invitation found yet!".tr,
style: const TextStyle(
color: CupertinoColors.secondaryLabel,
fontSize: 17,
),
),
)
: ListView.builder(
itemCount: controller.driverInvitationDataToPassengers.length,
itemBuilder: (context, index) {
return _buildInvitationItemPassengers(context, index);
},
),
);
}
Widget _buildInvitationItem(BuildContext context, int index) {
int countOfInvitDriver = int.tryParse(
(controller.driverInvitationData[index]['countOfInvitDriver'])
?.toString() ??
'0') ??
0;
double progressValue = (countOfInvitDriver / 100.0).clamp(0.0, 1.0);
return GestureDetector(
onTap: () {
controller.onSelectDriverInvitation(index);
},
child: Container(
margin: const EdgeInsets.symmetric(vertical: 8.0),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
(controller.driverInvitationData[index]['invitorName']),
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
color: CupertinoColors.label,
),
),
const SizedBox(height: 8),
ClipRRect(
borderRadius: BorderRadius.circular(4),
child: LinearProgressIndicator(
value: progressValue,
backgroundColor: CupertinoColors.systemGrey4,
valueColor:
const AlwaysStoppedAnimation<Color>(AppColor.blueColor),
minHeight: 6,
),
),
const SizedBox(height: 4),
Text(
'$countOfInvitDriver / 100 ${'Trip'.tr}',
style: const TextStyle(
fontSize: 13,
color: CupertinoColors.secondaryLabel,
),
),
],
),
),
);
}
Widget _buildInvitationItemPassengers(BuildContext context, int index) {
// Extracting the data from the sample JSON-like structure
var invitation = controller.driverInvitationDataToPassengers[index];
int countOfInvitDriver =
int.tryParse((invitation['countOfInvitDriver'])?.toString() ?? '0') ??
0;
double progressValue = (countOfInvitDriver / 10.0).clamp(0.0, 1.0);
return GestureDetector(
onTap: () {
controller.onSelectPassengerInvitation(index);
},
child: Container(
margin: const EdgeInsets.symmetric(vertical: 8.0),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
(invitation['passengerName'])
.toString(), // Handle null or missing data
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
color: CupertinoColors.label,
),
),
const SizedBox(height: 8),
ClipRRect(
borderRadius: BorderRadius.circular(4),
child: LinearProgressIndicator(
value: progressValue,
backgroundColor: CupertinoColors.systemGrey4,
valueColor:
const AlwaysStoppedAnimation<Color>(AppColor.blueColor),
minHeight: 6,
),
),
const SizedBox(height: 4),
Text(
'$countOfInvitDriver / 3 ${'Trip'.tr}', // Show trips completed
style: const TextStyle(
fontSize: 13,
color: CupertinoColors.secondaryLabel,
),
),
],
),
),
);
}
Widget _buildPassengerStats(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Your Rewards".tr,
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
color: CupertinoColors.label,
),
),
const SizedBox(height: 16),
_buildStatItem(
context,
"Total Invites".tr,
(controller.driverInvitationDataToPassengers[0]
['countOfInvitDriver']
.toString()),
),
_buildStatItem(
context,
"Active Users".tr,
(controller.driverInvitationDataToPassengers[0]['passengerName'])
.toString(),
),
],
),
);
}
Widget _buildStatItem(BuildContext context, String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
label,
style: const TextStyle(
color: CupertinoColors.label,
fontSize: 15,
),
),
Text(
value,
style: const TextStyle(
fontWeight: FontWeight.w600,
color: AppColor.blueColor,
fontSize: 15,
),
),
],
),
);
}
void _showContactsDialog(BuildContext context) {
showCupertinoModalPopup(
context: context,
builder: (BuildContext context) => Container(
height: 400,
color: CupertinoColors.systemBackground,
child: Column(
children: [
Container(
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: CupertinoColors.separator.withOpacity(0.5)),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CupertinoButton(
padding: EdgeInsets.zero,
child: Text('Cancel'.tr),
onPressed: () => Navigator.pop(context),
),
Text(
'Choose from contact'.tr,
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
),
),
const SizedBox(width: 60), // For balance
],
),
),
Expanded(
child: ListView.builder(
itemCount: controller.contactMaps.length,
itemBuilder: (context, index) {
final contact = controller.contactMaps[index];
return Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: CupertinoColors.separator.withOpacity(0.5),
),
),
),
child: CupertinoButton(
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 12),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
contact['name'],
style: const TextStyle(
color: CupertinoColors.label,
fontSize: 17,
),
),
Text(
controller.formatPhoneNumber(
contact['phones'][0].toString()),
style: const TextStyle(
color: CupertinoColors.secondaryLabel,
fontSize: 15,
),
),
],
),
),
const Icon(
CupertinoIcons.chevron_right,
color: CupertinoColors.secondaryLabel,
),
],
),
onPressed: () {
controller.selectPhone(contact['phones'].toString());
// Navigator.pop(context);
},
),
);
},
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,452 @@
import 'dart:io';
import 'package:sefer_driver/views/auth/captin/contact_us_page.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart';
import '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
import '../../../constant/info.dart';
import '../../../constant/style.dart';
import '../../../controller/auth/apple_sigin.dart';
import '../../../controller/auth/captin/login_captin_controller.dart';
import '../../../controller/auth/google_sign.dart';
import '../../../controller/functions/encrypt_decrypt.dart';
import '../../../controller/functions/overlay_permisssion.dart';
import '../../../main.dart';
import '../../../print.dart';
import '../../widgets/elevated_btn.dart';
import '../../widgets/mycircular.dart';
import '../country_widget.dart';
import 'otp_page.dart';
class LoginCaptin extends StatelessWidget {
final AuthController authController = Get.put(AuthController());
final LoginDriverController controller = Get.put(LoginDriverController());
LoginCaptin({super.key});
@override
Widget build(BuildContext context) {
return GetBuilder<LoginDriverController>(
builder: (controller) {
return Scaffold(
backgroundColor: AppColor.secondaryColor,
body: SafeArea(
child: Center(
child: _buildBodyContent(context, controller),
),
),
);
},
);
}
/// Determines which UI to show based on the driver's progress (agreement, permissions, login).
Widget _buildBodyContent(
BuildContext context, LoginDriverController controller) {
if (box.read(BoxName.agreeTerms) != 'agreed') {
return _buildAgreementPage(context, controller);
}
// if (box.read(BoxName.countryCode) == null) {
// return CountryPicker(); // Assumed to be a full-screen widget
// }
if (box.read(BoxName.locationPermission) != 'true') {
return _buildLocationPermissionPage(context, controller);
}
// Once all permissions are granted, show the main login UI
return PhoneNumberScreen();
}
/// Redesigned UI for the main login screen.
Widget _buildLoginUI(BuildContext context, LoginDriverController controller) {
return SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 32.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Image.asset('assets/images/logo.gif', height: 120, width: 120),
const SizedBox(height: 20),
Text(
'Driver Portal'.tr,
textAlign: TextAlign.center,
style: AppStyle.headTitle2.copyWith(fontSize: 28),
),
const SizedBox(height: 8),
Text(
'Sign in to start your journey'.tr,
textAlign: TextAlign.center,
style: AppStyle.subtitle,
),
const SizedBox(height: 40),
// Conditional UI based on the controller state
if (controller.isGoogleDashOpen)
_buildManualLoginForm(context, controller, isRegistration: true)
else if (Platform.isIOS && controller.isTest == 0)
_buildManualLoginForm(context, controller, isRegistration: false)
else
_buildSocialLoginOptions(context, controller),
const SizedBox(height: 32),
Center(
child: GestureDetector(
onTap: () => Get.to(() => ContactUsPage()),
child: Text(
'Need help? Contact Us'.tr,
style: AppStyle.subtitle.copyWith(
color: AppColor.blueColor,
decoration: TextDecoration.underline,
),
),
),
),
],
),
);
}
/// Builds the social login buttons (Google, Apple, and manual option).
Widget _buildSocialLoginOptions(
BuildContext context, LoginDriverController controller) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'Sign in with a provider for easy access'.tr,
textAlign: TextAlign.center,
style: AppStyle.title,
),
const SizedBox(height: 24),
_buildSocialButton(
text: 'Sign In with Google'.tr,
icon: FontAwesome.google,
backgroundColor: AppColor.redColor,
onPressed: () async {
GoogleSignInHelper().signInFromLogin();
},
),
if (Platform.isIOS) ...[
const SizedBox(height: 16),
_buildSocialButton(
text: 'Sign in with Apple'.tr,
icon: Icons.apple,
backgroundColor: Colors.black,
onPressed: () async {
User? user = await authController.signInWithApple();
if (user != null) {
box.write(BoxName.emailDriver, user.email.toString());
box.write(BoxName.driverID, user.uid);
controller.loginWithGoogleCredential(
user.uid,
user.email.toString(),
);
}
},
),
],
const SizedBox(height: 24),
Row(
children: [
const Expanded(child: Divider()),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text('Or'.tr, style: AppStyle.subtitle),
),
const Expanded(child: Divider()),
],
),
const SizedBox(height: 24),
MyElevatedButton(
title: 'Create Account with Email'.tr,
onPressed: () => controller.changeGoogleDashOpen(),
kolor: AppColor.blueColor, // Using 'kolor' as in your widget
),
],
);
}
/// Builds the form for manual email/password login or registration.
Widget _buildManualLoginForm(
BuildContext context, LoginDriverController controller,
{required bool isRegistration}) {
return Card(
elevation: 8,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Form(
key: controller.formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
isRegistration ? 'Create Driver Account'.tr : 'Driver Login'.tr,
textAlign: TextAlign.center,
style: AppStyle.headTitle2,
),
const SizedBox(height: 24),
_buildTextFormField(
controller: controller.emailController,
labelText: 'Email'.tr,
hintText: 'Enter your email'.tr,
prefixIcon: Icons.email_outlined,
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value == null || !GetUtils.isEmail(value)) {
return 'Please enter a valid email'.tr;
}
return null;
},
),
const SizedBox(height: 20),
GetBuilder<LoginDriverController>(
id: 'passwordVisibility', // ID to only rebuild this widget
builder: (_) => _buildTextFormField(
controller: controller.passwordController,
labelText: 'Password'.tr,
hintText: 'Enter your password'.tr,
prefixIcon: Icons.lock_outline,
obscureText: controller.isPasswordHidden,
suffixIcon: IconButton(
icon: Icon(
controller.isPasswordHidden
? Icons.visibility_off
: Icons.visibility,
color: AppColor.primaryColor,
),
onPressed: () => controller.togglePasswordVisibility(),
),
validator: (value) {
if (value == null || value.length < 6) {
return 'Password must be at least 6 characters'.tr;
}
return null;
},
),
),
const SizedBox(height: 30),
controller.isloading
? const Center(child: MyCircularProgressIndicator())
: MyElevatedButton(
title:
(isRegistration ? 'Create Account'.tr : 'Login'.tr),
onPressed: () {
if (controller.formKey.currentState!.validate()) {
if (isRegistration) {
String email = controller.emailController.text;
String uniqueId =
controller.generateUniqueIdFromEmail(email);
box.write(BoxName.driverID, uniqueId);
box.write(BoxName.emailDriver, email);
controller.loginUsingCredentialsWithoutGoogle(
controller.passwordController.text,
email,
);
} else {
// This is the flow for iOS manual login
controller.loginWithGoogleCredential(
controller.passwordController.text,
controller.emailController.text,
);
}
}
},
),
if (isRegistration) // Show back button only on the registration form
TextButton(
onPressed: () => controller.changeGoogleDashOpen(),
child: Text(
'Back to other sign-in options'.tr,
style: TextStyle(color: AppColor.primaryColor),
),
),
],
),
),
),
);
}
/// A helper method to create styled TextFormFields.
TextFormField _buildTextFormField({
required TextEditingController controller,
required String labelText,
required String hintText,
required IconData prefixIcon,
required String? Function(String?) validator,
bool obscureText = false,
Widget? suffixIcon,
TextInputType keyboardType = TextInputType.text,
}) {
return TextFormField(
controller: controller,
validator: validator,
obscureText: obscureText,
keyboardType: keyboardType,
decoration: InputDecoration(
labelText: labelText,
hintText: hintText,
prefixIcon: Icon(prefixIcon, color: AppColor.primaryColor),
suffixIcon: suffixIcon,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12.0)),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12.0),
borderSide:
const BorderSide(color: AppColor.primaryColor, width: 2.0),
),
),
);
}
/// A helper for creating consistent social login buttons.
Widget _buildSocialButton({
required String text,
required IconData icon,
required Color backgroundColor,
required VoidCallback onPressed,
}) {
return ElevatedButton.icon(
icon: Icon(icon, color: Colors.white),
label:
Text(text, style: const TextStyle(color: Colors.white, fontSize: 16)),
onPressed: onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: backgroundColor,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
padding: const EdgeInsets.symmetric(vertical: 14),
),
);
}
/// Redesigned UI for the Terms and Conditions agreement page.
Widget _buildAgreementPage(
BuildContext context, LoginDriverController controller) {
// This UI can be identical to the one in LoginPage for consistency.
// I am reusing the improved design from the previous request.
return Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.policy_outlined,
size: 80, color: AppColor.primaryColor),
const SizedBox(height: 20),
Text('Driver Agreement'.tr,
textAlign: TextAlign.center, style: AppStyle.headTitle2),
const SizedBox(height: 30),
RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: AppStyle.title.copyWith(height: 1.5),
children: [
TextSpan(
text:
"To become a driver, you must review and agree to the "
.tr),
TextSpan(
text: 'Terms of Use'.tr,
style: const TextStyle(
decoration: TextDecoration.underline,
color: AppColor.blueColor,
fontWeight: FontWeight.bold),
recognizer: TapGestureRecognizer()..onTap = () {}),
TextSpan(text: " and acknowledge our Privacy Policy.".tr),
],
),
),
Expanded(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade300),
borderRadius: BorderRadius.circular(8),
),
child: SingleChildScrollView(
padding: const EdgeInsets.all(12),
child: HtmlWidget(AppInformation.privacyPolicyArabic),
),
),
),
CheckboxListTile(
title: Text('I Agree'.tr, style: AppStyle.title),
value: controller.isAgreeTerms,
onChanged: (value) => controller.changeAgreeTerm(),
activeColor: AppColor.primaryColor,
controlAffinity: ListTileControlAffinity.leading,
),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
child: MyElevatedButton(
title: 'Continue'.tr,
onPressed: controller.isAgreeTerms
? () => controller.saveAgreementTerms()
: () {},
),
),
],
),
);
}
/// Redesigned UI for the Location Permission request page.
Widget _buildLocationPermissionPage(
BuildContext context, LoginDriverController controller) {
return Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Icon(Icons.location_on_outlined,
size: 80, color: AppColor.primaryColor),
const SizedBox(height: 20),
Text(
'Location Access Required'.tr,
textAlign: TextAlign.center,
style: AppStyle.headTitle2,
),
const SizedBox(height: 16),
Text(
'We need access to your location to match you with nearby passengers and provide accurate navigation.'
.tr,
textAlign: TextAlign.center,
style: AppStyle.title,
),
const SizedBox(height: 24),
Text(
'Please allow location access "all the time" to receive ride requests even when the app is in the background.'
.tr,
textAlign: TextAlign.center,
style: AppStyle.title.copyWith(
color: AppColor.greenColor, fontWeight: FontWeight.bold),
),
const SizedBox(height: 40),
MyElevatedButton(
title: "Allow Location Access".tr,
onPressed: () async {
await getLocationPermission(); // Assumes this function handles the request logic
if (await Permission.location.isGranted) {
box.write(BoxName.locationPermission, 'true');
controller.update(); // Re-check conditions
}
},
kolor: AppColor.greenColor,
),
const SizedBox(height: 16),
TextButton(
onPressed: () => openAppSettings(),
child: Text("Open Settings".tr,
style: const TextStyle(color: AppColor.blueColor)),
),
],
),
);
}
}

View File

@@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/controller/functions/log_out.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
class LogoutCaptain extends StatelessWidget {
const LogoutCaptain({super.key});
@override
Widget build(BuildContext context) {
return MyScafolld(
title: 'Log Out Page'.tr,
body: [
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MyElevatedButton(
title: 'Log Off'.tr,
onPressed: () {
LogOutController().logOutCaptain();
}),
const SizedBox(
height: 30,
),
MyElevatedButton(
title: 'Delete My Account'.tr,
onPressed: () {
LogOutController().deletecaptainAccount();
},
kolor: AppColor.redColor,
),
],
),
)
],
isleading: true,
);
}
}

View File

@@ -0,0 +1,488 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/controller/auth/captin/login_captin_controller.dart';
import '../../../constant/box_name.dart';
import '../../../controller/auth/captin/phone_helper_controller.dart';
import '../../../controller/local/phone_intel/intl_phone_field.dart';
import '../../../main.dart';
import '../../../print.dart';
class AuthScreen extends StatelessWidget {
final String title;
final String subtitle;
final Widget form;
// Using a more neutral, tech-themed animated logo
final String logoUrl = 'assets/images/logo.gif';
const AuthScreen({
super.key,
required this.title,
required this.subtitle,
required this.form,
});
@override
Widget build(BuildContext context) {
final controller = Get.put(LoginDriverController());
return Scaffold(
body: Container(
// UPDATED: Changed gradient colors to a green theme
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.teal.shade700, Colors.green.shade600],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(logoUrl, height: 120),
const SizedBox(height: 20),
Text(
title,
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.white),
),
const SizedBox(height: 10),
Text(
subtitle,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16, color: Colors.white.withOpacity(0.8)),
),
const SizedBox(height: 30),
GetBuilder<LoginDriverController>(builder: (context) {
return box.read(BoxName.isTest).toString() == '0'
? Container(
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.symmetric(
horizontal: 24, vertical: 32),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 12,
offset: const Offset(0, 6),
),
],
),
child: Form(
key: controller.formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Title
Text(
'Please login to continue',
style: TextStyle(
fontSize: 16,
color: Colors.grey[600],
),
textAlign: TextAlign.center,
),
const SizedBox(height: 32),
// Email Field
TextFormField(
keyboardType: TextInputType.emailAddress,
controller: controller.emailController,
decoration: InputDecoration(
labelText: 'Email'.tr,
hintText: 'Enter your email address'.tr,
prefixIcon:
const Icon(Icons.email_outlined),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
),
validator: (value) => value == null ||
value.isEmpty ||
!value.contains('@') ||
!value.contains('.')
? 'Enter a valid email'.tr
: null,
),
const SizedBox(height: 20),
// Password Field
TextFormField(
obscureText: true,
controller: controller.passwordController,
decoration: InputDecoration(
labelText: 'Password'.tr,
hintText: 'Enter your password'.tr,
prefixIcon: const Icon(Icons.lock_outline),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
),
validator: (value) =>
value == null || value.isEmpty
? 'Enter your password'.tr
: null,
),
const SizedBox(height: 24),
// Submit Button
GetBuilder<LoginDriverController>(
builder: (controller) => controller.isloading
? const Center(
child: CircularProgressIndicator())
: SizedBox(
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor:
Colors.blueAccent,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(12),
),
),
onPressed: () {
if (controller
.formKey.currentState!
.validate()) {
controller.logintest(
controller
.emailController.text
.trim(),
controller
.passwordController.text
.trim());
}
},
child: Text(
'Login'.tr,
style:
const TextStyle(fontSize: 16),
),
),
),
),
const SizedBox(height: 16),
// Optional: Forgot Password / Create Account
],
),
),
)
: Card(
elevation: 8,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.all(24.0),
child: form,
),
);
})
],
),
),
),
),
);
}
}
// --- UI Screens ---
class PhoneNumberScreen extends StatefulWidget {
const PhoneNumberScreen({super.key});
@override
State<PhoneNumberScreen> createState() => _PhoneNumberScreenState();
}
class _PhoneNumberScreenState extends State<PhoneNumberScreen> {
final _phoneController = TextEditingController();
final _formKey = GlobalKey<FormState>();
bool _isLoading = false;
void _submit() async {
if (_formKey.currentState!.validate()) {
setState(() => _isLoading = true);
final rawPhone = _phoneController.text.trim().replaceFirst('+', '');
final success = await PhoneAuthHelper.sendOtp(rawPhone);
if (success && mounted) {
Get.to(() => OtpVerificationScreen(phoneNumber: rawPhone));
}
if (mounted) setState(() => _isLoading = false);
}
}
@override
Widget build(BuildContext context) {
return AuthScreen(
title: 'welcome to intaleq'.tr,
subtitle: 'login or register subtitle'.tr,
form: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// TextFormField(
// controller: _phoneController,
// decoration: InputDecoration(
// labelText: 'phone number label'.tr,
// prefixIcon:
// Icon(Icons.phone_android, color: Colors.teal.shade400),
// border:
// OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
// ),
// keyboardType: TextInputType.phone,
// validator: (v) => v!.isEmpty ? 'phone number required'.tr : null,
// ),
Padding(
padding: const EdgeInsets.all(16.0),
child: IntlPhoneField(
// languageCode: 'ar',
showCountryFlag: false,
flagsButtonMargin: EdgeInsets.only(right: 8),
flagsButtonPadding: EdgeInsets.all(4),
dropdownDecoration:
BoxDecoration(borderRadius: BorderRadius.circular(8)),
// controller: _phoneController,
decoration: InputDecoration(
labelText: 'Phone Number'.tr,
border: const OutlineInputBorder(
borderSide: BorderSide(),
),
),
initialCountryCode: 'SY',
onChanged: (phone) {
// Properly concatenate country code and number
_phoneController.text = phone.completeNumber.toString();
Log.print(' phone.number: ${phone.number}');
print("Formatted phone number: ${_phoneController.text}");
},
validator: (phone) {
// Check if the phone number is not null and is valid
if (phone == null || phone.completeNumber.isEmpty) {
return 'Please enter your phone number';
}
// Extract the phone number (excluding the country code)
final number = phone.completeNumber.toString();
// Check if the number length is exactly 11 digits
if (number.length != 13) {
return 'Phone number must be exactly 11 digits long';
}
// If all validations pass, return null
return null;
},
),
),
const SizedBox(height: 24),
_isLoading
? const CircularProgressIndicator()
: ElevatedButton(
onPressed: _submit,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.teal,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
minimumSize: const Size(double.infinity, 50),
),
child: Text('send otp button'.tr),
),
],
),
),
);
}
}
class OtpVerificationScreen extends StatefulWidget {
final String phoneNumber;
const OtpVerificationScreen({super.key, required this.phoneNumber});
@override
State<OtpVerificationScreen> createState() => _OtpVerificationScreenState();
}
class _OtpVerificationScreenState extends State<OtpVerificationScreen> {
final _formKey = GlobalKey<FormState>();
final TextEditingController _otpControllers = TextEditingController();
// final List<FocusNode> _focusNodes = List.generate(5, (_) => FocusNode());
bool _isLoading = false;
@override
void dispose() {
// for (var controller in _otpControllers) {
// controller.dispose();
// }
// for (var node in _focusNodes) {
// node.dispose();
// }
super.dispose();
}
void _submit() async {
if (_formKey.currentState!.validate()) {
setState(() => _isLoading = true);
final otp = _otpControllers.text;
await PhoneAuthHelper.verifyOtp(widget.phoneNumber, otp);
if (mounted) setState(() => _isLoading = false);
}
}
Widget _buildOtpInput() {
return Form(
key: _formKey,
child: Container(
width: 200,
height: 50,
decoration: BoxDecoration(
color: Colors.grey.shade200,
shape: BoxShape.circle,
),
child: Center(
child: TextFormField(
controller: _otpControllers,
// focusNode: _focusNodes[index],
textAlign: TextAlign.center,
keyboardType: TextInputType.number,
maxLength: 5,
style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
decoration: const InputDecoration(
counterText: "",
border: InputBorder.none,
contentPadding: EdgeInsets.zero,
),
onChanged: (value) {},
validator: (v) => v!.isEmpty ? '' : null,
),
),
),
);
}
@override
Widget build(BuildContext context) {
return AuthScreen(
title: 'verify your number title'.tr,
subtitle:
'otp sent subtitle'.trParams({'phoneNumber': widget.phoneNumber}),
form: Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildOtpInput(),
const SizedBox(height: 30),
_isLoading
? const CircularProgressIndicator()
: ElevatedButton(
onPressed: _submit,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.teal,
foregroundColor: Colors.white,
minimumSize: const Size(double.infinity, 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
),
child: Text('verify and continue button'.tr),
),
],
),
);
}
}
class RegistrationScreen extends StatefulWidget {
final String phoneNumber;
const RegistrationScreen({super.key, required this.phoneNumber});
@override
State<RegistrationScreen> createState() => _RegistrationScreenState();
}
class _RegistrationScreenState extends State<RegistrationScreen> {
final _formKey = GlobalKey<FormState>();
final _firstNameController = TextEditingController();
final _lastNameController = TextEditingController();
final _emailController = TextEditingController();
bool _isLoading = false;
void _submit() async {
if (_formKey.currentState!.validate()) {
setState(() => _isLoading = true);
await PhoneAuthHelper.registerUser(
phoneNumber: widget.phoneNumber,
firstName: _firstNameController.text.trim(),
lastName: _lastNameController.text.trim(),
email: _emailController.text.trim(),
);
if (mounted) setState(() => _isLoading = false);
}
}
@override
Widget build(BuildContext context) {
return AuthScreen(
title: 'one last step title'.tr,
subtitle: 'complete profile subtitle'.tr,
form: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
controller: _firstNameController,
decoration: InputDecoration(
labelText: 'first name label'.tr,
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12)))),
validator: (v) => v!.isEmpty ? 'first name required'.tr : null,
),
const SizedBox(height: 16),
TextFormField(
controller: _lastNameController,
decoration: InputDecoration(
labelText: 'last name label'.tr,
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12)))),
validator: (v) => v!.isEmpty ? 'last name required'.tr : null,
),
const SizedBox(height: 16),
TextFormField(
controller: _emailController,
decoration: InputDecoration(
labelText: 'email optional label'.tr,
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12)))),
keyboardType: TextInputType.emailAddress,
),
const SizedBox(height: 24),
_isLoading
? const CircularProgressIndicator()
: ElevatedButton(
onPressed: _submit,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.teal,
foregroundColor: Colors.white,
minimumSize: const Size(double.infinity, 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
),
child: Text('complete registration button'.tr),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,176 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../controller/auth/captin/opt_token_controller.dart';
class OtpVerificationPage extends StatefulWidget {
final String phone;
final String deviceToken;
final String token;
final String ptoken;
const OtpVerificationPage({
super.key,
required this.phone,
required this.deviceToken,
required this.token,
required this.ptoken,
});
@override
State<OtpVerificationPage> createState() => _OtpVerificationPageState();
}
class _OtpVerificationPageState extends State<OtpVerificationPage> {
late final OtpVerificationController controller;
final List<FocusNode> _focusNodes = List.generate(6, (index) => FocusNode());
final List<TextEditingController> _textControllers =
List.generate(6, (index) => TextEditingController());
@override
void initState() {
super.initState();
controller = Get.put(OtpVerificationController(
phone: widget.phone,
deviceToken: widget.deviceToken,
token: widget.token,
));
}
@override
void dispose() {
for (var node in _focusNodes) {
node.dispose();
}
for (var controller in _textControllers) {
controller.dispose();
}
super.dispose();
}
void _onOtpChanged(String value, int index) {
if (value.isNotEmpty) {
if (index < 5) {
_focusNodes[index + 1].requestFocus();
} else {
_focusNodes[index].unfocus(); // إلغاء التركيز بعد آخر حقل
}
} else if (index > 0) {
_focusNodes[index - 1].requestFocus();
}
// تجميع نصوص كل الحقول لتكوين الرمز النهائي
controller.otpCode.value = _textControllers.map((c) => c.text).join();
}
Widget _buildOtpInputFields() {
return Directionality(
textDirection: TextDirection.ltr, // لضمان ترتيب الحقول من اليسار لليمين
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: List.generate(5, (index) {
return SizedBox(
width: 45,
height: 55,
child: TextFormField(
controller: _textControllers[index],
focusNode: _focusNodes[index],
textAlign: TextAlign.center,
keyboardType: TextInputType.number,
maxLength: 1,
style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
decoration: InputDecoration(
counterText: "",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.grey.shade300),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: Theme.of(context).primaryColor, width: 2),
),
),
onChanged: (value) => _onOtpChanged(value, index),
),
);
}),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Verify OTP'.tr),
backgroundColor: Colors.transparent,
elevation: 0,
centerTitle: true,
),
backgroundColor: Colors.grey[50],
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 20),
Icon(Icons.phonelink_lock_rounded,
size: 80, color: Theme.of(context).primaryColor),
const SizedBox(height: 24),
Text(
'Verification Code'.tr,
style:
const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Text(
'${'We have sent a verification code to your mobile number:'.tr} ${widget.phone}',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.grey.shade600, fontSize: 16, height: 1.5),
),
),
const SizedBox(height: 40),
_buildOtpInputFields(),
const SizedBox(height: 40),
Obx(() => SizedBox(
width: double.infinity,
height: 50,
child: controller.isVerifying.value
? const Center(child: CircularProgressIndicator())
: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12))),
onPressed: () =>
controller.verifyOtp(widget.ptoken),
child: Text('Verify'.tr,
style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.w600)),
),
)),
const SizedBox(height: 24),
Obx(
() => controller.canResend.value
? TextButton(
onPressed: controller.sendOtp,
child: Text('Resend Code'.tr),
)
: Text(
'${'You can resend in'.tr} ${controller.countdown.value} ${'seconds'.tr}',
style: const TextStyle(color: Colors.grey),
),
)
],
),
),
),
);
}
}

View File

@@ -0,0 +1,202 @@
import 'package:sefer_driver/constant/style.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/controller/auth/captin/register_captin_controller.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import '../../../constant/colors.dart';
import '../../../controller/auth/google_sign.dart';
class RegisterCaptin extends StatelessWidget {
const RegisterCaptin({super.key});
@override
Widget build(BuildContext context) {
Get.put(RegisterCaptainController());
return MyScafolld(
title: 'Register Driver'.tr,
body: [
// GetBuilder<RegisterCaptainController>(
// builder: (controller) => Form(
// key: controller.formKey,
// child: Padding(
// padding: const EdgeInsets.all(16.0),
// child: SingleChildScrollView(
// child: Container(
// decoration: const BoxDecoration(
// boxShadow: [
// BoxShadow(
// offset: Offset(3, 3),
// color: AppColor.accentColor,
// blurRadius: 3)
// ],
// color: AppColor.secondaryColor,
// ),
// child: Padding(
// padding: const EdgeInsets.all(16),
// child: Column(
// children: [
// SizedBox(
// width: Get.width * .8,
// child: TextFormField(
// keyboardType: TextInputType.emailAddress,
// controller: controller.emailController,
// decoration: InputDecoration(
// focusedBorder: OutlineInputBorder(
// borderSide: const BorderSide(
// color: AppColor.primaryColor,
// width: 2.0,
// ),
// borderRadius: BorderRadius.circular(10),
// ),
// fillColor: AppColor.accentColor,
// hoverColor: AppColor.accentColor,
// focusColor: AppColor.accentColor,
// border: const OutlineInputBorder(
// borderRadius:
// BorderRadius.all(Radius.circular(12))),
// labelText: 'Email'.tr,
// hintText: 'Enter your email address'.tr,
// ),
// validator: (value) {
// if (value!.isEmpty ||
// (!value.contains('@') ||
// !value.contains('.'))) {
// return 'Please enter Your Email.'.tr;
// }
// return null;
// },
// ),
// ),
// const SizedBox(
// height: 15,
// ),
// SizedBox(
// width: Get.width * .8,
// child: TextFormField(
// obscureText: true,
// keyboardType: TextInputType.emailAddress,
// controller: controller.passwordController,
// decoration: InputDecoration(
// focusedBorder: OutlineInputBorder(
// borderSide: const BorderSide(
// color: AppColor.primaryColor,
// width: 2.0,
// ),
// borderRadius: BorderRadius.circular(10),
// ),
// fillColor: AppColor.accentColor,
// hoverColor: AppColor.accentColor,
// focusColor: AppColor.accentColor,
// border: const OutlineInputBorder(
// borderRadius:
// BorderRadius.all(Radius.circular(12))),
// labelText: 'Password'.tr,
// hintText: 'Enter your Password'.tr,
// ),
// validator: (value) {
// if (value!.isEmpty) {
// return 'Please enter Your Password.'.tr;
// }
// if (value.length < 6) {
// return 'Password must br at least 6 character.'
// .tr;
// }
// return null;
// },
// ),
// ),
// const SizedBox(
// height: 15,
// ),
// SizedBox(
// width: Get.width * .8,
// child: TextFormField(
// keyboardType: TextInputType.phone,
// cursorColor: AppColor.accentColor,
// controller: controller.phoneController,
// decoration: InputDecoration(
// focusedBorder: OutlineInputBorder(
// borderSide: const BorderSide(
// color: AppColor.primaryColor,
// width: 2.0,
// ),
// borderRadius: BorderRadius.circular(10),
// ),
// focusColor: AppColor.accentColor,
// fillColor: AppColor.accentColor,
// border: const OutlineInputBorder(
// borderRadius:
// BorderRadius.all(Radius.circular(12))),
// labelText: 'Phone'.tr,
// hintText: 'Enter your phone number'.tr,
// ),
// validator: (value) {
// if (value!.isEmpty || value.length != 10) {
// return 'Please enter your phone number.'.tr;
// }
// return null;
// },
// ),
// ),
// const SizedBox(
// height: 15,
// ),
// MyElevatedButton(
// title: 'Next'.tr,
// onPressed: () => controller.nextToAIDetection()),
// ],
// ),
// ),
// ),
// ),
// ),
// ),
// )
Image.asset(
'assets/images/on1.png',
fit: BoxFit.cover,
height: double.maxFinite,
width: double.maxFinite,
),
Center(
child: Container(
decoration: AppStyle.boxDecoration1,
height: Get.height * .7,
width: Get.width * .9,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Image.asset(
'assets/images/logo.gif',
height: Get.width * .3,
width: Get.width * .3,
fit: BoxFit.fill,
),
Container(
decoration: AppStyle.boxDecoration1,
height: Get.height * .3,
width: Get.width * .8,
child: Center(
child: Text(
'Sign in with Google for easier email and name entry'.tr,
textAlign: TextAlign.center,
style: AppStyle.title,
),
),
),
MyElevatedButton(
title: 'Sign In by Google'.tr,
onPressed: () async {
// await GoogleSignInHelper.signIn();
},
kolor: AppColor.blueColor,
),
],
),
))
],
isleading: true);
}
}

View File

@@ -0,0 +1,91 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/controller/auth/captin/register_captin_controller.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
class VerifyEmailCaptainPage extends StatelessWidget {
VerifyEmailCaptainPage({super.key});
RegisterCaptainController registerCaptinController =
Get.put(RegisterCaptainController());
@override
Widget build(BuildContext context) {
return MyScafolld(
title: 'Verify Email For Driver'.tr,
body: [
Positioned(
top: 10,
left: 20,
child: Text(
'We sent 5 digit to your Email provided'.tr,
style: AppStyle.title.copyWith(fontSize: 20),
)),
GetBuilder<RegisterCaptainController>(
builder: (controller) => Positioned(
top: 100,
left: 80,
right: 80,
child: Padding(
padding: const EdgeInsets.all(10),
child: Column(
children: [
SizedBox(
width: 100,
child: TextField(
controller: registerCaptinController.verifyCode,
decoration: InputDecoration(
labelStyle: AppStyle.title,
border: const OutlineInputBorder(),
hintText: '5 digit'.tr,
counterStyle: AppStyle.number,
hintStyle: AppStyle.subtitle
.copyWith(color: AppColor.accentColor),
),
maxLength: 5,
keyboardType: TextInputType.number,
),
),
const SizedBox(
height: 30,
),
MyElevatedButton(
title: 'Send Verfication Code'.tr,
onPressed: () {
registerCaptinController.sendVerifications();
})
],
),
),
)),
],
isleading: true,
);
}
Padding verifyEmail() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: AppColor.accentColor,
width: 2,
),
borderRadius: BorderRadius.circular(8),
),
child: const Padding(
padding: EdgeInsets.all(10),
child: SizedBox(
width: 20,
child: TextField(
maxLength: 1,
keyboardType: TextInputType.number,
),
),
),
),
);
}
}

View File

@@ -0,0 +1,185 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constant/box_name.dart';
import '../../constant/colors.dart';
import '../../constant/style.dart';
import '../../controller/auth/captin/login_captin_controller.dart';
import '../../controller/profile/profile_controller.dart';
import '../../main.dart';
import '../widgets/elevated_btn.dart';
import 'captin/login_captin.dart';
class CountryPicker extends StatelessWidget {
final ProfileController controller = Get.put(ProfileController());
final List<String> countryOptions = [
'Jordan',
'USA',
'Egypt',
'Turkey',
'Saudi Arabia',
'Qatar',
'Bahrain',
'Kuwait',
];
CountryPicker({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return GetBuilder<ProfileController>(builder: (controller) {
return Padding(
padding: const EdgeInsets.all(20),
child: ListView(
children: [
const SizedBox(
height: 20,
),
Text(
"Select Your Country".tr,
style: AppStyle.headTitle2,
textAlign: TextAlign.center,
),
// const SizedBox(
// height: 20,
// ),
Padding(
padding: const EdgeInsets.all(10),
child: Text(
"To ensure you receive the most accurate information for your location, please select your country below. This will help tailor the app experience and content to your country."
.tr,
style: AppStyle.title,
textAlign: TextAlign.center,
),
),
SizedBox(
height: 200,
child: CupertinoPicker(
itemExtent: 32,
onSelectedItemChanged: (int index) {
controller.setCountry(countryOptions[index]);
box.write(BoxName.countryCode,
countryOptions[index]); // Save in English
},
children: List.generate(
countryOptions.length,
(index) => Center(
child: Text(
countryOptions[index]
.tr, // Display translated if not English
style: AppStyle.title,
),
),
),
),
),
MyElevatedButton(
title: 'Select Country'.tr, // Use translated text for button
onPressed: () {
Get.find<LoginDriverController>().saveCountryCode(controller
.selectedCountry
.toString()); // No conversion needed
box.write(
BoxName.countryCode, //
controller.selectedCountry); // Already saved in English
Get.snackbar(controller.selectedCountry.toString().tr, '');
Get.off(LoginCaptin());
},
)
],
),
);
});
}
}
class CountryPickerFromSetting extends StatelessWidget {
final ProfileController controller = Get.put(ProfileController());
final LoginDriverController loginController =
Get.put(LoginDriverController());
final List<String> countryOptions = [
'Jordan',
'Egypt',
'Turkey',
'Saudi Arabia',
'Qatar',
'Bahrain',
'Kuwait',
'USA',
];
CountryPickerFromSetting({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return GetBuilder<ProfileController>(builder: (controller) {
return Padding(
padding: const EdgeInsets.all(20.0),
child: ListView(
children: [
const SizedBox(
height: 20,
),
Text(
"Select Your Country".tr,
style: AppStyle.headTitle2,
textAlign: TextAlign.center,
),
// const SizedBox(
// height: 20,
// ),
Padding(
padding: const EdgeInsets.all(10),
child: Text(
"To ensure you receive the most accurate information for your location, please select your country below. This will help tailor the app experience and content to your country."
.tr,
style: AppStyle.title,
textAlign: TextAlign.center,
),
),
SizedBox(
height: 200,
child: CupertinoPicker(
itemExtent: 32,
onSelectedItemChanged: (int index) {
controller.setCountry(countryOptions[index]);
box.write(BoxName.countryCode,
countryOptions[index]); // Save in English
},
children: List.generate(
countryOptions.length,
(index) => Center(
child: Text(
countryOptions[index]
.tr, // Display translated if not English
style: AppStyle.title,
),
),
),
),
),
MyElevatedButton(
title: 'Select Country'.tr, // Use translated text for button
onPressed: () async {
loginController.saveCountryCode(controller.selectedCountry
.toString()); // No conversion needed
box.write(
BoxName.countryCode, //
controller.selectedCountry); // Already saved in English
Get.snackbar(controller.selectedCountry.toString().tr, '',
backgroundColor: AppColor.greenColor);
// Get.back();//
// Get.back();
},
)
],
),
);
});
}
}

295
lib/views/auth/register_page.dart Executable file
View File

@@ -0,0 +1,295 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/controller/auth/register_controller.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import '../../constant/colors.dart';
class RegisterPage extends StatelessWidget {
const RegisterPage({super.key});
@override
Widget build(BuildContext context) {
Get.put(RegisterController());
return MyScafolld(
title: 'Register'.tr,
body: [
GetBuilder<RegisterController>(
builder: (controller) => Form(
key: controller.formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Container(
decoration: const BoxDecoration(
boxShadow: [
BoxShadow(
offset: Offset(3, 3),
color: AppColor.accentColor,
blurRadius: 3)
],
color: AppColor.secondaryColor,
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: Get.width * .4,
child: TextFormField(
keyboardType: TextInputType.text,
controller: controller.firstNameController,
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(
color: AppColor.primaryColor,
width: 2.0,
),
borderRadius: BorderRadius.circular(10),
),
fillColor: AppColor.accentColor,
hoverColor: AppColor.accentColor,
focusColor: AppColor.accentColor,
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(12))),
labelText: 'First name'.tr,
hintText: 'Enter your first name'.tr,
),
validator: (value) {
if (value!.isEmpty) {
return 'Please enter your first name.'.tr;
}
return null;
},
),
),
SizedBox(
width: Get.width * .4,
child: TextFormField(
keyboardType: TextInputType.text,
controller: controller.lastNameController,
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(
color: AppColor.primaryColor,
width: 2.0,
),
borderRadius: BorderRadius.circular(10),
),
focusColor: AppColor.accentColor,
fillColor: AppColor.accentColor,
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(12))),
labelText: 'Last name'.tr,
hintText: 'Enter your last name'.tr,
),
validator: (value) {
if (value!.isEmpty) {
return 'Please enter your last name.'.tr;
}
return null;
},
),
),
],
),
const SizedBox(
height: 15,
),
TextFormField(
keyboardType: TextInputType.emailAddress,
controller: controller.emailController,
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(
color: AppColor.primaryColor,
width: 2.0,
),
borderRadius: BorderRadius.circular(10),
),
fillColor: AppColor.accentColor,
hoverColor: AppColor.accentColor,
focusColor: AppColor.accentColor,
border: const OutlineInputBorder(
borderRadius:
BorderRadius.all(Radius.circular(12))),
labelText: 'Email'.tr,
hintText: 'Enter your email address'.tr,
),
validator: (value) {
if (value!.isEmpty ||
(!value.contains('@') ||
!value.contains('.'))) {
return 'Please enter Your Email.'.tr;
}
return null;
},
),
const SizedBox(
height: 15,
),
TextFormField(
obscureText: true,
keyboardType: TextInputType.emailAddress,
controller: controller.passwordController,
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(
color: AppColor.primaryColor,
width: 2.0,
),
borderRadius: BorderRadius.circular(10),
),
fillColor: AppColor.accentColor,
hoverColor: AppColor.accentColor,
focusColor: AppColor.accentColor,
border: const OutlineInputBorder(
borderRadius:
BorderRadius.all(Radius.circular(12))),
labelText: 'Password'.tr,
hintText: 'Enter your Password'.tr,
),
validator: (value) {
if (value!.isEmpty) {
return 'Please enter Your Password.'.tr;
}
if (value.length < 6) {
return 'Password must br at least 6 character.'
.tr;
}
return null;
},
),
const SizedBox(
height: 15,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: Get.width * .4,
child: TextFormField(
keyboardType: TextInputType.phone,
cursorColor: AppColor.accentColor,
controller: controller.phoneController,
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(
color: AppColor.primaryColor,
width: 2.0,
),
borderRadius: BorderRadius.circular(10),
),
focusColor: AppColor.accentColor,
fillColor: AppColor.accentColor,
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(12))),
labelText: 'Phone'.tr,
hintText: 'Enter your phone number'.tr,
),
validator: (value) {
if (value!.isEmpty || value.length != 10) {
return 'Please enter your phone number.'.tr;
}
return null;
},
),
),
SizedBox(
width: Get.width * .4,
child: TextFormField(
keyboardType: TextInputType.text,
controller: controller.siteController,
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(
color: AppColor.primaryColor,
width: 2.0,
),
borderRadius: BorderRadius.circular(10),
),
focusColor: AppColor.accentColor,
fillColor: AppColor.accentColor,
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(12))),
labelText: 'City'.tr,
hintText: 'Enter your City'.tr,
),
validator: (value) {
if (value!.isEmpty) {
return 'Please enter your City.'.tr;
}
return null;
},
),
),
],
),
const SizedBox(
height: 15,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: () => controller.getBirthDate(),
child: Container(
height: 50,
width: Get.width * .4,
decoration: BoxDecoration(
border: Border.all(),
borderRadius: BorderRadius.circular(13)),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20),
child: Text(
controller.birthDate,
style: AppStyle.title,
),
),
),
),
// DropdownButton(
// value: controller.gender,
// items: [
// DropdownMenuItem(
// value: 'Male'.tr,
// child: Text('Male'.tr),
// ),
// DropdownMenuItem(
// value: 'Female'.tr,
// child: Text('Female'.tr),
// ),
// DropdownMenuItem(
// value: '--'.tr,
// child: Text('--'.tr),
// ),
// ],
// onChanged: (value) {
// controller.changeGender(value!);
// },
// )
],
),
MyElevatedButton(
title: 'Register'.tr,
onPressed: () => controller.register())
]),
),
),
),
),
),
)
],
isleading: true);
}
}

View File

@@ -0,0 +1,90 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/controller/auth/register_controller.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
class VerifyEmailPage extends StatelessWidget {
const VerifyEmailPage({super.key});
@override
Widget build(BuildContext context) {
Get.put(RegisterController());
return MyScafolld(
title: 'Verify Email'.tr,
body: [
Positioned(
top: 10,
left: 20,
right: 20,
child: Text(
'We sent 5 digit to your Email provided'.tr,
style: AppStyle.title.copyWith(fontSize: 20),
)),
GetBuilder<RegisterController>(
builder: (controller) => Positioned(
top: 100,
left: 80,
right: 80,
child: Padding(
padding: const EdgeInsets.all(10),
child: Column(
children: [
SizedBox(
width: 100,
child: TextField(
controller: controller.verfyCode,
decoration: InputDecoration(
labelStyle: AppStyle.title,
border: const OutlineInputBorder(),
hintText: '5 digit'.tr,
counterStyle: AppStyle.number,
hintStyle: AppStyle.subtitle
.copyWith(color: AppColor.accentColor),
),
maxLength: 5,
keyboardType: TextInputType.number,
),
),
const SizedBox(
height: 30,
),
MyElevatedButton(
title: 'Send Verfication Code'.tr,
onPressed: () => controller.sendVerifications())
],
),
),
)),
],
isleading: true,
);
}
Padding verifyEmail() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: AppColor.accentColor,
width: 2,
),
borderRadius: BorderRadius.circular(8),
),
child: const Padding(
padding: EdgeInsets.all(10),
child: SizedBox(
width: 20,
child: TextField(
maxLength: 1,
keyboardType: TextInputType.number,
),
),
),
),
);
}
}