25-7-28-2
This commit is contained in:
144
lib/views/Rate/rate_app_page.dart
Executable file
144
lib/views/Rate/rate_app_page.dart
Executable file
@@ -0,0 +1,144 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../controller/rate/rate_app_controller.dart';
|
||||
|
||||
class RatingScreen extends StatelessWidget {
|
||||
final RatingController ratingController = Get.put(RatingController());
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text(
|
||||
"Rate Our App".tr,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
child: Center(
|
||||
child: Obx(() {
|
||||
return Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
// App logo or visual
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Image.asset(
|
||||
'assets/images/logo.gif',
|
||||
height: 120,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Rating Prompt
|
||||
Text(
|
||||
"How would you rate our app?".tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.navTitleTextStyle
|
||||
.copyWith(fontSize: 18, fontWeight: FontWeight.w600),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Comment Box
|
||||
CupertinoTextField(
|
||||
controller: ratingController.comment,
|
||||
placeholder: 'Write your comment here'.tr,
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: CupertinoColors.systemGrey4),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: CupertinoColors.white,
|
||||
),
|
||||
prefix: Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: Icon(CupertinoIcons.pencil_ellipsis_rectangle,
|
||||
color: CupertinoColors.systemGrey),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Star Rating Section
|
||||
_buildRatingStars(),
|
||||
|
||||
const SizedBox(height: 30),
|
||||
|
||||
// Submit Button
|
||||
CupertinoButton(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24, vertical: 14),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: CupertinoColors.activeGreen,
|
||||
onPressed: () {
|
||||
if (ratingController.userRating.value > 0) {
|
||||
ratingController
|
||||
.submitRating(ratingController.userRating.value);
|
||||
Get.snackbar(
|
||||
"Thank You!".tr,
|
||||
"Your rating has been submitted.".tr,
|
||||
backgroundColor: CupertinoColors.systemGrey6,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
margin: const EdgeInsets.all(16),
|
||||
borderRadius: 12,
|
||||
);
|
||||
} else {
|
||||
Get.snackbar(
|
||||
"Error".tr,
|
||||
"Please select a rating before submitting.".tr,
|
||||
backgroundColor: CupertinoColors.systemRed,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
margin: const EdgeInsets.all(16),
|
||||
borderRadius: 12,
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
"Submit Rating".tr,
|
||||
style: const TextStyle(
|
||||
color: CupertinoColors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Widget for building rating stars with animations
|
||||
Widget _buildRatingStars() {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: List.generate(5, (index) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
ratingController.userRating.value = index + 1;
|
||||
},
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: Icon(
|
||||
CupertinoIcons.star_fill,
|
||||
size: 40,
|
||||
color: index < ratingController.userRating.value
|
||||
? CupertinoColors.systemYellow
|
||||
: CupertinoColors.systemGrey3,
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
258
lib/views/Rate/rate_passenger.dart
Executable file
258
lib/views/Rate/rate_passenger.dart
Executable file
@@ -0,0 +1,258 @@
|
||||
import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart';
|
||||
import 'package:sefer_driver/views/widgets/my_textField.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/constant/colors.dart';
|
||||
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
|
||||
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
||||
|
||||
import '../../constant/style.dart';
|
||||
import '../../controller/rate/rate_conroller.dart';
|
||||
|
||||
class RatePassenger extends StatelessWidget {
|
||||
final RateController controller = Get.put(RateController());
|
||||
|
||||
RatePassenger({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MyScafolld(
|
||||
title: 'Rate Passenger'.tr,
|
||||
body: [
|
||||
GetBuilder<RateController>(builder: (controller) {
|
||||
return Positioned(
|
||||
top: 40,
|
||||
left: Get.width * .1,
|
||||
right: Get.width * .1,
|
||||
child: Container(
|
||||
decoration: AppStyle.boxDecoration,
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Container(
|
||||
height: Get.height * .25,
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'${'Total price from '.tr}${Get.find<MapDriverController>().passengerName}',
|
||||
style: AppStyle.title,
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 2,
|
||||
color: AppColor.redColor,
|
||||
)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
(double.parse(controller.price.toString()) -
|
||||
double.parse(controller.price
|
||||
.toString()) *
|
||||
.12)
|
||||
.toStringAsFixed(2),
|
||||
style: AppStyle.number.copyWith(
|
||||
color: AppColor.redColor,
|
||||
textBaseline: TextBaseline.ideographic,
|
||||
decoration: TextDecoration.lineThrough,
|
||||
decorationColor: AppColor.redColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 2,
|
||||
color: AppColor.greenColor,
|
||||
)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
Get.find<MapDriverController>()
|
||||
.paymentAmount,
|
||||
style: AppStyle.number,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Text(
|
||||
'Exclusive offers and discounts always with the Sefer app'
|
||||
.tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppStyle.title
|
||||
.copyWith(color: AppColor.redColor),
|
||||
)
|
||||
],
|
||||
)),
|
||||
),
|
||||
controller.walletChecked != 'true'
|
||||
? controller.ispassengerWantWalletFromDriver
|
||||
? Container(
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
"How much Passenger pay?".tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
Form(
|
||||
key: controller.formKey,
|
||||
child: MyTextForm(
|
||||
controller:
|
||||
controller.passengerPayAmount,
|
||||
label: "passenger amount to me".tr,
|
||||
hint: "passenger amount to me".tr,
|
||||
type: const TextInputType
|
||||
.numberWithOptions(decimal: true),
|
||||
),
|
||||
),
|
||||
MyElevatedButton(
|
||||
title: "Press here".tr,
|
||||
onPressed: () {
|
||||
controller.addPassengerWallet();
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
width: Get.width * .73,
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
"Would the passenger like to settle the remaining fare using their wallet?"
|
||||
.tr,
|
||||
style: AppStyle.title,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
MyElevatedButton(
|
||||
title: "Press here".tr,
|
||||
onPressed: () {
|
||||
controller.passengerWantPay();
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Center(
|
||||
child: RatingBar.builder(
|
||||
initialRating: 0,
|
||||
itemCount: 5,
|
||||
itemSize: 50,
|
||||
itemPadding: const EdgeInsets.symmetric(horizontal: 2),
|
||||
itemBuilder: (context, index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return const Icon(
|
||||
Icons.sentiment_very_dissatisfied,
|
||||
color: Colors.red,
|
||||
);
|
||||
case 1:
|
||||
return const Icon(
|
||||
Icons.sentiment_dissatisfied,
|
||||
color: Colors.redAccent,
|
||||
);
|
||||
case 2:
|
||||
return const Icon(
|
||||
Icons.sentiment_neutral,
|
||||
color: Colors.amber,
|
||||
);
|
||||
case 3:
|
||||
return const Icon(
|
||||
Icons.sentiment_satisfied,
|
||||
color: Colors.lightGreen,
|
||||
);
|
||||
case 4:
|
||||
return const Icon(
|
||||
Icons.sentiment_very_satisfied,
|
||||
color: Colors.green,
|
||||
);
|
||||
default:
|
||||
return const Icon(
|
||||
Icons.sentiment_neutral,
|
||||
color: Colors.amber,
|
||||
);
|
||||
} //
|
||||
},
|
||||
onRatingUpdate: (rating) {
|
||||
controller.selectRateItem(rating);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
SizedBox(
|
||||
width: Get.width * .75,
|
||||
child: TextFormField(
|
||||
maxLines: 4,
|
||||
minLines: 1,
|
||||
keyboardType: TextInputType.multiline,
|
||||
controller: controller.comment,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Enter your Note'.tr,
|
||||
hintText: 'Type something...',
|
||||
prefixIcon: const Icon(
|
||||
Icons.rate_review), // Add an icon as a prefix
|
||||
suffixIcon: IconButton(
|
||||
icon: const Icon(
|
||||
Icons.clear,
|
||||
color: AppColor.redColor,
|
||||
), // Add an icon as a suffix
|
||||
onPressed: () {
|
||||
controller.comment.clear();
|
||||
},
|
||||
),
|
||||
border:
|
||||
const OutlineInputBorder(), // Add a border around the input field
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
Colors.blue), // Customize the border color
|
||||
),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors
|
||||
.green), // Customize the border color when focused
|
||||
),
|
||||
errorBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors
|
||||
.red), // Customize the border color when there's an error
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
MyElevatedButton(
|
||||
title: 'Submit rating'.tr,
|
||||
onPressed: () => controller.addRateToPassenger())
|
||||
],
|
||||
),
|
||||
));
|
||||
}),
|
||||
],
|
||||
isleading: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
484
lib/views/Rate/ride_calculate_driver.dart
Executable file
484
lib/views/Rate/ride_calculate_driver.dart
Executable file
@@ -0,0 +1,484 @@
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/constant/colors.dart';
|
||||
import 'package:sefer_driver/constant/info.dart';
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
||||
import 'package:sefer_driver/views/widgets/mycircular.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../../controller/home/captin/duration_controller .dart';
|
||||
|
||||
class RideCalculateDriver extends StatelessWidget {
|
||||
RideCalculateDriver({super.key});
|
||||
// DurationController durationController = Get.put(DurationController());
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(DurationController());
|
||||
return MyScafolld(
|
||||
title: 'Ride Summaries'.tr,
|
||||
body: [
|
||||
Center(
|
||||
child: GetBuilder<DurationController>(
|
||||
builder: (durationController) => durationController.isLoading
|
||||
? const Center(child: MyCircularProgressIndicator())
|
||||
: durationController.jsonData1.isEmpty ||
|
||||
durationController.jsonData2.isEmpty
|
||||
? Center(
|
||||
child: Text('No data yet!'.tr),
|
||||
)
|
||||
: ListView(
|
||||
// mainAxisAlignment: MainAxisAlignment.start,
|
||||
// crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'${'Average of Hours of'.tr} ${AppInformation.appName}${' is ON for this month'.tr}${' ${durationController.jsonData1['message'][0]['day'].toString().split('-')[1]}'.tr}',
|
||||
style: AppStyle.title,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(6),
|
||||
child: Container(
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
height: Get.height * .4,
|
||||
child: LineChart(
|
||||
duration: const Duration(milliseconds: 150),
|
||||
curve: Curves.ease,
|
||||
LineChartData(
|
||||
lineBarsData: [
|
||||
LineChartBarData(
|
||||
isStepLineChart: true,
|
||||
spots: durationController.chartData,
|
||||
isCurved: true,
|
||||
color: Colors
|
||||
.deepPurpleAccent, // Custom color
|
||||
barWidth: 3, // Thinner line
|
||||
dotData: const FlDotData(
|
||||
show:
|
||||
true), // Show dots on each point
|
||||
belowBarData: BarAreaData(
|
||||
// Add gradient fill below the line
|
||||
show: true,
|
||||
color: AppColor.deepPurpleAccent,
|
||||
),
|
||||
isStrokeJoinRound: true,
|
||||
shadow: const BoxShadow(
|
||||
color: AppColor.yellowColor,
|
||||
blurRadius: 4,
|
||||
offset: Offset(2, 2),
|
||||
),
|
||||
),
|
||||
],
|
||||
showingTooltipIndicators: const [],
|
||||
titlesData: FlTitlesData(
|
||||
show: true,
|
||||
topTitles: AxisTitles(
|
||||
axisNameWidget: Text(
|
||||
'Days'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
axisNameSize: 30,
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
axisNameWidget: Text(
|
||||
'Total Hours on month'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
axisNameSize: 30,
|
||||
sideTitles: const SideTitles(
|
||||
reservedSize: 30,
|
||||
showTitles: true)),
|
||||
leftTitles: AxisTitles(
|
||||
axisNameWidget: Text(
|
||||
'Counts of Hours on days'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
axisNameSize: 30,
|
||||
sideTitles: const SideTitles(
|
||||
reservedSize: 30,
|
||||
showTitles: true)),
|
||||
),
|
||||
gridData: const FlGridData(
|
||||
show: true,
|
||||
),
|
||||
borderData: FlBorderData(
|
||||
show: true,
|
||||
border: const Border(
|
||||
bottom: BorderSide(
|
||||
color: AppColor.accentColor),
|
||||
left: BorderSide(
|
||||
color: AppColor.accentColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(6),
|
||||
child: Container(
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
height: Get.height * .4,
|
||||
child: LineChart(
|
||||
duration: const Duration(milliseconds: 150),
|
||||
curve: Curves.ease,
|
||||
LineChartData(
|
||||
lineBarsData: [
|
||||
LineChartBarData(
|
||||
spots: durationController.chartRideCount,
|
||||
// isCurved: true,
|
||||
color: Colors
|
||||
.deepPurpleAccent, // Custom color
|
||||
barWidth: 3, // Thinner line
|
||||
dotData: const FlDotData(
|
||||
show:
|
||||
true), // Show dots on each point
|
||||
belowBarData: BarAreaData(
|
||||
// Add gradient fill below the line
|
||||
show: true,
|
||||
color: AppColor.deepPurpleAccent,
|
||||
),
|
||||
isStrokeJoinRound: true,
|
||||
shadow: const BoxShadow(
|
||||
color: AppColor.yellowColor,
|
||||
blurRadius: 4,
|
||||
offset: Offset(2, 2),
|
||||
),
|
||||
),
|
||||
],
|
||||
showingTooltipIndicators: const [],
|
||||
titlesData: FlTitlesData(
|
||||
show: true,
|
||||
topTitles: AxisTitles(
|
||||
axisNameWidget: Text(
|
||||
'Days'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
axisNameSize: 30,
|
||||
// sideTitles: const SideTitles(
|
||||
// reservedSize: 30, showTitles: true),
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
axisNameWidget: Text(
|
||||
'${"Total rides on month".tr} = ${durationController.jsonData2['message'][0]['totalCount'].toString()}'
|
||||
.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
axisNameSize: 30,
|
||||
sideTitles: const SideTitles(
|
||||
reservedSize: 30,
|
||||
showTitles: true)),
|
||||
leftTitles: AxisTitles(
|
||||
axisNameWidget: Text(
|
||||
'Counts of rides on days'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
axisNameSize: 30,
|
||||
sideTitles: const SideTitles(
|
||||
reservedSize: 30,
|
||||
showTitles: true)),
|
||||
),
|
||||
gridData: const FlGridData(
|
||||
show: true,
|
||||
),
|
||||
borderData: FlBorderData(
|
||||
show: true,
|
||||
border: const Border(
|
||||
bottom: BorderSide(
|
||||
color: AppColor.accentColor),
|
||||
left: BorderSide(
|
||||
color: AppColor.accentColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(6),
|
||||
child: Container(
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
height: Get.height * .4,
|
||||
child: LineChart(
|
||||
duration: const Duration(milliseconds: 150),
|
||||
curve: Curves.ease,
|
||||
LineChartData(
|
||||
lineBarsData: [
|
||||
LineChartBarData(
|
||||
isStepLineChart: true,
|
||||
spots: durationController
|
||||
.chartRidePriceDriver,
|
||||
isCurved: true,
|
||||
isStrokeCapRound: true,
|
||||
preventCurveOverShooting: true,
|
||||
color: Colors
|
||||
.deepPurpleAccent, // Custom color
|
||||
barWidth: 3, // Thinner line
|
||||
dotData: const FlDotData(
|
||||
show:
|
||||
true), // Show dots on each point
|
||||
belowBarData: BarAreaData(
|
||||
// Add gradient fill below the line
|
||||
show: true,
|
||||
color: AppColor.deepPurpleAccent,
|
||||
),
|
||||
isStrokeJoinRound: true,
|
||||
shadow: const BoxShadow(
|
||||
color: AppColor.yellowColor,
|
||||
blurRadius: 4,
|
||||
offset: Offset(2, 2),
|
||||
),
|
||||
),
|
||||
],
|
||||
showingTooltipIndicators: const [],
|
||||
titlesData: FlTitlesData(
|
||||
show: true,
|
||||
topTitles: AxisTitles(
|
||||
axisNameWidget: Text(
|
||||
'Days'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
axisNameSize: 30,
|
||||
// sideTitles: const SideTitles(
|
||||
// reservedSize: 30, showTitles: true),
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
axisNameWidget: Text(
|
||||
'${"Total budgets on month".tr} = ${durationController.jsonData2['message'][0]['totalPrice'].toString()}'
|
||||
.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
axisNameSize: 30,
|
||||
sideTitles: const SideTitles(
|
||||
reservedSize: 30,
|
||||
showTitles: true)),
|
||||
leftTitles: AxisTitles(
|
||||
axisNameWidget: Text(
|
||||
'Counts of budgets on days'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
axisNameSize: 30,
|
||||
sideTitles: const SideTitles(
|
||||
reservedSize: 30,
|
||||
showTitles: true)),
|
||||
),
|
||||
gridData: const FlGridData(
|
||||
show: true,
|
||||
),
|
||||
borderData: FlBorderData(
|
||||
show: true,
|
||||
border: const Border(
|
||||
bottom: BorderSide(
|
||||
color: AppColor.accentColor),
|
||||
left: BorderSide(
|
||||
color: AppColor.accentColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Container(
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
child: durationController.monthlyList.isEmpty
|
||||
? SizedBox(
|
||||
height: Get.height * .2,
|
||||
child: Center(
|
||||
child: Text(
|
||||
"No data yet".tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
)
|
||||
: DriverStatsTable(
|
||||
monthlyList:
|
||||
durationController.monthlyList,
|
||||
)))
|
||||
],
|
||||
),
|
||||
)
|
||||
// BarChartWidget(),
|
||||
),
|
||||
],
|
||||
isleading: true);
|
||||
}
|
||||
}
|
||||
|
||||
class DriverStatsTable extends StatelessWidget {
|
||||
final List monthlyList;
|
||||
|
||||
const DriverStatsTable({Key? key, required this.monthlyList})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: Colors.white,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.5),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 5,
|
||||
offset: const Offset(0, 3),
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: DataTable(
|
||||
columnSpacing: 12,
|
||||
columns: [
|
||||
DataColumn(label: Text('Statistic'.tr)),
|
||||
DataColumn(label: Text('Value'.tr)),
|
||||
],
|
||||
rows: _buildRows(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<DataRow> _buildRows() {
|
||||
return [
|
||||
DataRow(cells: [
|
||||
DataCell(Text(
|
||||
'Total Orders'.tr,
|
||||
style: AppStyle.title,
|
||||
)),
|
||||
DataCell(Text(
|
||||
monthlyList[0]['total_orders'].toString(),
|
||||
style: AppStyle.number,
|
||||
)),
|
||||
]),
|
||||
DataRow(cells: [
|
||||
DataCell(Text(
|
||||
'Completed'.tr,
|
||||
style: AppStyle.title,
|
||||
)),
|
||||
DataCell(Text(
|
||||
monthlyList[0]['completed_orders'].toString(),
|
||||
style: AppStyle.number,
|
||||
)),
|
||||
]),
|
||||
DataRow(cells: [
|
||||
DataCell(Text(
|
||||
'Canceled Orders'.tr,
|
||||
style: AppStyle.title,
|
||||
)),
|
||||
DataCell(Text(
|
||||
monthlyList[0]['canceled_orders'].toString(),
|
||||
style: AppStyle.number,
|
||||
)),
|
||||
]),
|
||||
DataRow(cells: [
|
||||
DataCell(Text(
|
||||
'Rejected Orders'.tr,
|
||||
style: AppStyle.title,
|
||||
)),
|
||||
DataCell(Text(
|
||||
monthlyList[0]['rejected_orders'].toString(),
|
||||
style: AppStyle.number,
|
||||
)),
|
||||
]),
|
||||
DataRow(cells: [
|
||||
DataCell(Text(
|
||||
'Percent Rejected'.tr,
|
||||
style: AppStyle.title,
|
||||
)),
|
||||
DataCell(Text(
|
||||
'${(monthlyList[0]['percent_rejected']).toString()}%',
|
||||
style: AppStyle.number.copyWith(
|
||||
color: double.parse(monthlyList[0]['percent_canceled']) < .3
|
||||
? AppColor.greenColor
|
||||
: AppColor.redColor),
|
||||
)),
|
||||
]),
|
||||
DataRow(cells: [
|
||||
DataCell(Text(
|
||||
'Percent Canceled'.tr,
|
||||
style: AppStyle.title,
|
||||
)),
|
||||
DataCell(Text(
|
||||
'${(monthlyList[0]['percent_canceled']).toString()}%',
|
||||
style: AppStyle.number.copyWith(
|
||||
color: double.parse(monthlyList[0]['percent_canceled']) < .3
|
||||
? AppColor.greenColor
|
||||
: AppColor.redColor),
|
||||
)),
|
||||
]),
|
||||
DataRow(cells: [
|
||||
DataCell(Text(
|
||||
'Percent Completed'.tr,
|
||||
style: AppStyle.title,
|
||||
)),
|
||||
DataCell(Text(
|
||||
'${(monthlyList[0]['percent_completed']).toString()}%',
|
||||
style: AppStyle.number.copyWith(
|
||||
color: double.parse(monthlyList[0]['percent_completed']) > .7
|
||||
? AppColor.greenColor
|
||||
: AppColor.redColor),
|
||||
)),
|
||||
]),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class StaticDriverOrder extends StatelessWidget {
|
||||
const StaticDriverOrder({
|
||||
Key? key,
|
||||
required this.title,
|
||||
required this.jsonTitle,
|
||||
}) : super(key: key);
|
||||
|
||||
final String title, jsonTitle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.all(8.0),
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.3),
|
||||
spreadRadius: 3,
|
||||
blurRadius: 5,
|
||||
offset: const Offset(0, 3), // changes position of shadow
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
Text(
|
||||
jsonTitle,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
977
lib/views/auth/captin/ai_page.dart
Executable file
977
lib/views/auth/captin/ai_page.dart
Executable 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 Driver’s 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();
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
83
lib/views/auth/captin/camera_widgets/camera_lisence_page.dart
Executable file
83
lib/views/auth/captin/camera_widgets/camera_lisence_page.dart
Executable 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
169
lib/views/auth/captin/car_license_page.dart
Executable file
169
lib/views/auth/captin/car_license_page.dart
Executable 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,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// )
|
||||
// ],
|
||||
// ));
|
||||
// }
|
||||
80
lib/views/auth/captin/cards/gemini_egypt.dart
Executable file
80
lib/views/auth/captin/cards/gemini_egypt.dart
Executable 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 {}
|
||||
}
|
||||
}
|
||||
221
lib/views/auth/captin/cards/sms_signup.dart
Executable file
221
lib/views/auth/captin/cards/sms_signup.dart
Executable 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
987
lib/views/auth/captin/cards/syrian_card_a_i.dart
Executable file
987
lib/views/auth/captin/cards/syrian_card_a_i.dart
Executable 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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
100
lib/views/auth/captin/contact_us_page.dart
Executable file
100
lib/views/auth/captin/contact_us_page.dart
Executable 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
211
lib/views/auth/captin/criminal_documents_page.dart
Executable file
211
lib/views/auth/captin/criminal_documents_page.dart
Executable 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 driver’s 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 driver’s 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,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
110
lib/views/auth/captin/driver_car_controller.dart
Executable file
110
lib/views/auth/captin/driver_car_controller.dart
Executable 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();
|
||||
}
|
||||
}
|
||||
0
lib/views/auth/captin/forget.dart
Executable file
0
lib/views/auth/captin/forget.dart
Executable file
640
lib/views/auth/captin/invite_driver_screen.dart
Executable file
640
lib/views/auth/captin/invite_driver_screen.dart
Executable 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);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
452
lib/views/auth/captin/login_captin.dart
Executable file
452
lib/views/auth/captin/login_captin.dart
Executable 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)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
42
lib/views/auth/captin/logout_captain.dart
Executable file
42
lib/views/auth/captin/logout_captain.dart
Executable 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
488
lib/views/auth/captin/otp_page.dart
Normal file
488
lib/views/auth/captin/otp_page.dart
Normal 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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
176
lib/views/auth/captin/otp_token_page.dart
Normal file
176
lib/views/auth/captin/otp_token_page.dart
Normal 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),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
202
lib/views/auth/captin/register_captin.dart
Executable file
202
lib/views/auth/captin/register_captin.dart
Executable 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);
|
||||
}
|
||||
}
|
||||
91
lib/views/auth/captin/verify_email_captain.dart
Executable file
91
lib/views/auth/captin/verify_email_captain.dart
Executable 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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
185
lib/views/auth/country_widget.dart
Executable file
185
lib/views/auth/country_widget.dart
Executable 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
295
lib/views/auth/register_page.dart
Executable 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);
|
||||
}
|
||||
}
|
||||
90
lib/views/auth/verify_email_page.dart
Executable file
90
lib/views/auth/verify_email_page.dart
Executable 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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
101
lib/views/home/Captin/About Us/about_us.dart
Executable file
101
lib/views/home/Captin/About Us/about_us.dart
Executable file
@@ -0,0 +1,101 @@
|
||||
import 'package:sefer_driver/constant/box_name.dart';
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
import 'package:sefer_driver/main.dart';
|
||||
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class AboutPage extends StatelessWidget {
|
||||
const AboutPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MyScafolld(
|
||||
title: 'About Us'.tr,
|
||||
body: [
|
||||
// Company Logo (consider adding an image asset)
|
||||
ListView(
|
||||
children: [
|
||||
Center(
|
||||
child: Image.asset(
|
||||
'assets/images/logo.png', // Replace with your logo image asset path
|
||||
height: 100.0,
|
||||
width: 100.0,
|
||||
),
|
||||
), // Company Name and Location
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
'SEFER LLC\n${box.read(BoxName.countryCode).toString().tr}',
|
||||
style: AppStyle.headTitle2,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Text(
|
||||
'SEFER is a ride-sharing app designed with your safety and affordability in mind. We connect you with reliable drivers in your area, ensuring a convenient and stress-free travel experience.\n\nHere are some of the key features that set us apart:'
|
||||
.tr,
|
||||
style: AppStyle.title,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
), // Security Features List
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.lock, color: Colors.blue),
|
||||
const SizedBox(width: 8.0),
|
||||
Text(
|
||||
'Most Secure Methods'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.phone, color: Colors.blue),
|
||||
const SizedBox(width: 8.0),
|
||||
Text(
|
||||
'In-App VOIP Calls'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.videocam, color: Colors.blue),
|
||||
const SizedBox(width: 8.0),
|
||||
Text(
|
||||
'Recorded Trips for Safety'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
), // Affordability Highlight
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Text(
|
||||
'\nWe also prioritize affordability, offering competitive pricing to make your rides accessible.'
|
||||
.tr,
|
||||
style: AppStyle.title,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// About Us Text
|
||||
],
|
||||
isleading: true);
|
||||
}
|
||||
}
|
||||
145
lib/views/home/Captin/About Us/frequantly_question.dart
Executable file
145
lib/views/home/Captin/About Us/frequantly_question.dart
Executable file
@@ -0,0 +1,145 @@
|
||||
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
||||
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../../constant/style.dart';
|
||||
|
||||
class FrequentlyQuestionsPage extends StatelessWidget {
|
||||
const FrequentlyQuestionsPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String selectedPayment = 'cash'; // Replace with your initial selection
|
||||
bool canCancelRide = false;
|
||||
return MyScafolld(
|
||||
title: 'Frequently Questions'.tr,
|
||||
body: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ListView(
|
||||
children: [
|
||||
// Question 1: How do I request a ride?
|
||||
ExpansionTile(
|
||||
title: Text(
|
||||
'How do I request a ride?'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
children: [
|
||||
Text(
|
||||
'Step-by-step instructions on how to request a ride through the Intaleq app.'
|
||||
.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// Question 2: What types of vehicles are available?
|
||||
ExpansionTile(
|
||||
title: Text(
|
||||
'What types of vehicles are available?'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
children: [
|
||||
Text(
|
||||
'Intaleq offers a variety of vehicle options to suit your needs, including economy, comfort, and luxury. Choose the option that best fits your budget and passenger count.'
|
||||
.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// Question 3: How can I pay for my ride?
|
||||
ExpansionTile(
|
||||
title: Text(
|
||||
'How can I pay for my ride?'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
children: [
|
||||
Text(
|
||||
'Intaleq offers multiple payment methods for your convenience. Choose between cash payment or credit/debit card payment during ride confirmation.'
|
||||
.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// Question 4: Can I cancel my ride? (if applicable)
|
||||
ExpansionTile(
|
||||
title: Text(
|
||||
'Can I cancel my ride?'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
children: [
|
||||
Text(
|
||||
'Yes, you can cancel your ride under certain conditions (e.g., before driver is assigned). See the Intaleq cancellation policy for details.'
|
||||
.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// Question 5 & 6: Driver-specific questions
|
||||
ExpansionTile(
|
||||
title: Text(
|
||||
'Driver Registration & Requirements'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
children: [
|
||||
Text(
|
||||
'${'How can I register as a driver?'.tr}\n${'What are the requirements to become a driver?'.tr}',
|
||||
style: AppStyle.title,
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
MyDialog().getDialog('title', 'midTitle', () {
|
||||
; //todo add in this dialog papers for driver
|
||||
});
|
||||
},
|
||||
child: Text(
|
||||
'Visit our website or contact Intaleq support for information on driver registration and requirements.'
|
||||
.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// Question 7: How do I communicate with the other party?
|
||||
ExpansionTile(
|
||||
title: Text(
|
||||
'How do I communicate with the other party (passenger/driver)?'
|
||||
.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
children: [
|
||||
Text(
|
||||
'Intaleq provides in-app chat functionality to allow you to communicate with your driver or passenger during your ride.'
|
||||
.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// Question 8: What safety measures does Intaleq offer?
|
||||
ExpansionTile(
|
||||
title: Text(
|
||||
'What safety measures does Intaleq offer?'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
children: [
|
||||
Text(
|
||||
'Intaleq prioritizes your safety. We offer features like driver verification, in-app trip tracking, and emergency contact options.'
|
||||
.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
isleading: true);
|
||||
}
|
||||
}
|
||||
157
lib/views/home/Captin/About Us/settings_captain.dart
Executable file
157
lib/views/home/Captin/About Us/settings_captain.dart
Executable file
@@ -0,0 +1,157 @@
|
||||
import 'package:sefer_driver/constant/colors.dart';
|
||||
import 'package:sefer_driver/controller/profile/setting_controller.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
import 'package:sefer_driver/views/lang/languages.dart';
|
||||
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
||||
|
||||
import '../../../../controller/functions/vibrate.dart';
|
||||
import '../../../auth/country_widget.dart';
|
||||
import 'about_us.dart';
|
||||
import 'frequantly_question.dart';
|
||||
import 'using_app_page.dart';
|
||||
|
||||
class SettingsCaptain extends StatelessWidget {
|
||||
const SettingsCaptain({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(SettingController());
|
||||
Get.put(HomePageController());
|
||||
return MyScafolld(
|
||||
title: 'Settings'.tr,
|
||||
body: [
|
||||
ListView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
children: <Widget>[
|
||||
// General Section
|
||||
_buildSectionHeader('General'.tr),
|
||||
CupertinoListSection(
|
||||
margin: EdgeInsets.zero,
|
||||
children: [
|
||||
CupertinoListTile(
|
||||
leading: const Icon(CupertinoIcons.globe),
|
||||
title: Text('Language'.tr, style: AppStyle.headTitle2),
|
||||
subtitle: Text('You can change the language of the app'.tr,
|
||||
style: AppStyle.subtitle),
|
||||
trailing: const CupertinoListTileChevron(),
|
||||
onTap: () => Get.to(const Language()),
|
||||
),
|
||||
CupertinoListTile(
|
||||
leading: const Icon(CupertinoIcons.flag_fill),
|
||||
title: Text('Change Country'.tr, style: AppStyle.headTitle2),
|
||||
subtitle: Text(
|
||||
'You can change the Country to get all features'.tr,
|
||||
style: AppStyle.subtitle),
|
||||
trailing: const CupertinoListTileChevron(),
|
||||
onTap: () => Get.to(
|
||||
MyScafolld(
|
||||
title: 'Change Country'.tr,
|
||||
body: [CountryPickerFromSetting()],
|
||||
isleading: true,
|
||||
// isCupertino: true, // Indicate it's a Cupertino style page
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// App Preferences Section
|
||||
_buildSectionHeader('App Preferences'.tr),
|
||||
CupertinoListSection(
|
||||
margin: EdgeInsets.zero,
|
||||
children: [
|
||||
CupertinoListTile(
|
||||
leading: Icon(
|
||||
CupertinoIcons.map_pin_ellipse,
|
||||
color: AppColor.redColor,
|
||||
),
|
||||
title: Text('Google Map App'.tr, style: AppStyle.headTitle2),
|
||||
subtitle: Text(
|
||||
'If you want to make Google Map App run directly when you apply order'
|
||||
.tr,
|
||||
style: AppStyle.subtitle,
|
||||
),
|
||||
trailing: GetBuilder<SettingController>(
|
||||
builder: (settingController) {
|
||||
return CupertinoSwitch(
|
||||
value: settingController.isGoogleMapsEnabled,
|
||||
activeTrackColor: AppColor.primaryColor,
|
||||
onChanged: (bool value) {
|
||||
settingController.onChangMapApp();
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
CupertinoListTile(
|
||||
leading: Icon(Icons.vibration),
|
||||
title: Text('Vibration'.tr, style: AppStyle.headTitle2),
|
||||
subtitle: Text(
|
||||
"You can change the vibration feedback for all buttons".tr,
|
||||
style: AppStyle.subtitle,
|
||||
),
|
||||
trailing: GetBuilder<HomePageController>(
|
||||
builder: (controller) => CupertinoSwitch(
|
||||
value: controller.isVibrate,
|
||||
onChanged: controller.changeVibrateOption,
|
||||
activeTrackColor: AppColor.primaryColor,
|
||||
),
|
||||
),
|
||||
onTap: () => print('3'),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// Help & Support Section
|
||||
_buildSectionHeader('Help & Support'.tr),
|
||||
CupertinoListSection(
|
||||
margin: EdgeInsets.zero,
|
||||
children: [
|
||||
CupertinoListTile(
|
||||
leading: const Icon(CupertinoIcons.question_circle_fill),
|
||||
title: Text('Frequently Questions'.tr,
|
||||
style: AppStyle.headTitle2),
|
||||
trailing: const CupertinoListTileChevron(),
|
||||
onTap: () => Get.to(() => const FrequentlyQuestionsPage()),
|
||||
),
|
||||
CupertinoListTile(
|
||||
leading: const Icon(CupertinoIcons.hand_raised_fill),
|
||||
title:
|
||||
Text("How to use Intaleq".tr, style: AppStyle.headTitle2),
|
||||
trailing: const CupertinoListTileChevron(),
|
||||
onTap: () => Get.to(() => const UsingAppPage()),
|
||||
),
|
||||
CupertinoListTile(
|
||||
leading: const Icon(CupertinoIcons.info_circle_fill),
|
||||
title: Text('About Us'.tr, style: AppStyle.headTitle2),
|
||||
trailing: const CupertinoListTileChevron(),
|
||||
onTap: () => Get.to(() => const AboutPage()),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
],
|
||||
isleading: true,
|
||||
// isCupertino: true, // Indicate this screen is generally Cupertino style
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSectionHeader(String title) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0, top: 20.0, bottom: 10.0),
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 17.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: CupertinoColors.secondaryLabel,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
112
lib/views/home/Captin/About Us/using_app_page.dart
Executable file
112
lib/views/home/Captin/About Us/using_app_page.dart
Executable file
@@ -0,0 +1,112 @@
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
||||
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class UsingAppPage extends StatelessWidget {
|
||||
const UsingAppPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MyScafolld(
|
||||
title: "How to use Intaleq".tr,
|
||||
body: [
|
||||
SizedBox(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ListView(
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
MyDialogContent().getDialog(
|
||||
"What are the order details we provide to you?".tr,
|
||||
Image.network(
|
||||
'https://api.Intaleq-egypt.com/Intaleq/imageForUsingApp/order_page.jpg',
|
||||
height: 300,
|
||||
width: 300,
|
||||
fit: BoxFit.cover,
|
||||
), () {
|
||||
Get.back();
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
"What are the order details we provide to you?".tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
MyDialog().getDialog(
|
||||
"What are the order details we provide to you?".tr,
|
||||
'''Intaleq Wallet Features:
|
||||
|
||||
Transfer money multiple times.
|
||||
Transfer to anyone.
|
||||
Make purchases.
|
||||
Charge your account.
|
||||
Charge a friend's Intaleq account.
|
||||
Store your money with us and receive it in your bank as a monthly salary.'''
|
||||
.tr, () {
|
||||
Get.back();
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
"What is the feature of our wallet?".tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
MyDialog().getDialog(
|
||||
"What is Types of Trips in Intaleq?".tr,
|
||||
'''Types of Trips in Intaleq:
|
||||
|
||||
Comfort: For cars newer than 2017 with air conditioning.
|
||||
Lady: For girl drivers.
|
||||
Speed: For fixed salary and endpoints.
|
||||
Mashwari: For flexible trips where passengers choose the car and driver with prior arrangements.
|
||||
Raih Gai: For same-day return trips longer than 50km.
|
||||
'''
|
||||
.tr, () {
|
||||
Get.back();
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
"What is Types of Trips in Intaleq?".tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
isleading: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
193
lib/views/home/Captin/About Us/video_page.dart
Executable file
193
lib/views/home/Captin/About Us/video_page.dart
Executable file
@@ -0,0 +1,193 @@
|
||||
import 'package:sefer_driver/constant/info.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
import 'package:youtube_player_flutter/youtube_player_flutter.dart';
|
||||
|
||||
import '../../../../controller/home/captin/help/video_controller.dart';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class VideoListPage extends StatelessWidget {
|
||||
final VideoController videoController = Get.put(VideoController());
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text('Videos Tutorials'.tr),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0,
|
||||
vertical: 10.0), // Outer padding around the list
|
||||
child: GetBuilder<VideoController>(
|
||||
builder: (videoController) {
|
||||
return ListView.builder(
|
||||
itemCount: videoController.videos.length,
|
||||
itemBuilder: (context, index) {
|
||||
final video = videoController.videos[index];
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(
|
||||
vertical: 8.0), // Spacing between each card
|
||||
decoration: BoxDecoration(
|
||||
color: CupertinoColors.white,
|
||||
borderRadius:
|
||||
BorderRadius.circular(12.0), // Rounded corners
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: CupertinoColors.systemGrey.withOpacity(0.3),
|
||||
offset: const Offset(
|
||||
0, 4), // Offset for shadow to appear below
|
||||
blurRadius: 10.0, // Blur for softer shadow effect
|
||||
),
|
||||
],
|
||||
border: Border.all(
|
||||
color: CupertinoColors.systemGrey4,
|
||||
width: 0.5, // Subtle border for a refined iOS-like look
|
||||
),
|
||||
),
|
||||
child: CupertinoListTile(
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 4.0),
|
||||
child: Text(
|
||||
video['title'],
|
||||
style: const TextStyle(
|
||||
color: CupertinoColors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
video['description'],
|
||||
style: const TextStyle(
|
||||
color: CupertinoColors.systemGrey,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
// Navigate to video player page (iOS-style)
|
||||
Get.to(() => VideoPlayerPage1(videoUrl: video['url']));
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class VideoPlayerPage extends StatelessWidget {
|
||||
final String videoUrl;
|
||||
|
||||
VideoPlayerPage({required this.videoUrl});
|
||||
|
||||
final VideoController videoController = Get.put(VideoController());
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Initialize the video when the page is loaded
|
||||
videoController.initializeVideo(videoUrl);
|
||||
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: const CupertinoNavigationBar(
|
||||
middle: Text(AppInformation.appName),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Center(
|
||||
child: GetBuilder<VideoController>(
|
||||
builder: (controller) {
|
||||
if (!controller.videoPlayerController.value.isInitialized) {
|
||||
return const CircularProgressIndicator(); // Show loading indicator while initializing
|
||||
}
|
||||
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// Video player widget
|
||||
AspectRatio(
|
||||
aspectRatio:
|
||||
controller.videoPlayerController.value.aspectRatio,
|
||||
// child: VideoPlayer(controller.videoPlayerController),
|
||||
child: VideoPlayer(controller.videoPlayerController),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Play/pause button
|
||||
CupertinoButton(
|
||||
onPressed: () {
|
||||
if (controller.videoPlayerController.value.isPlaying) {
|
||||
controller.pause();
|
||||
} else {
|
||||
controller.play();
|
||||
}
|
||||
},
|
||||
child: Icon(
|
||||
controller.videoPlayerController.value.isPlaying
|
||||
? CupertinoIcons.pause
|
||||
: CupertinoIcons.play_arrow,
|
||||
color: CupertinoColors.activeBlue,
|
||||
size: 30,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class VideoPlayerPage1 extends StatelessWidget {
|
||||
final String videoUrl;
|
||||
|
||||
VideoPlayerPage1({required this.videoUrl});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Extract the video ID from the URL
|
||||
String videoId = YoutubePlayer.convertUrlToId(videoUrl)!;
|
||||
|
||||
// Create a YoutubePlayerController
|
||||
final YoutubePlayerController _controller = YoutubePlayerController(
|
||||
initialVideoId: videoId,
|
||||
flags: const YoutubePlayerFlags(
|
||||
autoPlay: true,
|
||||
loop: true,
|
||||
mute: false,
|
||||
captionLanguage: 'ar',
|
||||
),
|
||||
);
|
||||
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
// Full-screen YouTube player
|
||||
Positioned.fill(
|
||||
child: YoutubePlayer(
|
||||
controller: _controller,
|
||||
showVideoProgressIndicator: true,
|
||||
),
|
||||
),
|
||||
// Overlay back button in the top left corner for exit
|
||||
Positioned(
|
||||
top: 40.0,
|
||||
left: 16.0,
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.arrow_back, color: Colors.white),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
231
lib/views/home/Captin/assurance_health_page.dart
Executable file
231
lib/views/home/Captin/assurance_health_page.dart
Executable file
@@ -0,0 +1,231 @@
|
||||
import 'package:sefer_driver/constant/box_name.dart';
|
||||
import 'package:sefer_driver/main.dart';
|
||||
import 'package:sefer_driver/views/widgets/error_snakbar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../controller/home/captin/help/assurance_controller.dart';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class AssuranceHealthPage extends StatelessWidget {
|
||||
AssuranceHealthPage({super.key});
|
||||
AssuranceHealthController assuranceHealthController =
|
||||
Get.put(AssuranceHealthController());
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text("Health Insurance".tr),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: GetBuilder<AssuranceHealthController>(
|
||||
builder: (assuranceHealthController) {
|
||||
return Column(
|
||||
children: [
|
||||
Text(
|
||||
"When you complete 500 trips, you will be eligible for exclusive health insurance offers."
|
||||
.tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
CupertinoButton.filled(
|
||||
child: Text("Show My Trip Count".tr),
|
||||
onPressed: () async {
|
||||
assuranceHealthController.getTripCountByCaptain();
|
||||
},
|
||||
),
|
||||
_buildTripCountAvatar(
|
||||
assuranceHealthController.tripCount['count'] == null
|
||||
? '0'
|
||||
: assuranceHealthController.tripCount['count']
|
||||
.toString(),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: CupertinoColors.systemGrey6,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(14),
|
||||
child: Text(
|
||||
"We have partnered with health insurance providers to offer you special health coverage. Complete 500 trips and receive a 20% discount on health insurance premiums."
|
||||
.tr,
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.textStyle
|
||||
.copyWith(fontWeight: FontWeight.w600),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
CupertinoButton.filled(
|
||||
disabledColor: AppColor.blueColor,
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Text(
|
||||
"Would you like to proceed with health insurance?".tr,
|
||||
style: const TextStyle(
|
||||
fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
onPressed: () async {
|
||||
// Show confirmation dialog before proceeding
|
||||
showCupertinoDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
// Variable to store the health insurance provider chosen by the driver
|
||||
TextEditingController providerController =
|
||||
TextEditingController();
|
||||
|
||||
return CupertinoAlertDialog(
|
||||
title: Text(
|
||||
"Confirmation".tr,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold, fontSize: 18),
|
||||
),
|
||||
content: Column(
|
||||
children: [
|
||||
Text(
|
||||
"Would you like to proceed with health insurance?"
|
||||
.tr,
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
CupertinoTextField(
|
||||
controller: providerController,
|
||||
placeholder:
|
||||
"Do you have a disease for a long time?".tr,
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: CupertinoColors.systemGrey,
|
||||
width: 1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: <Widget>[
|
||||
CupertinoDialogAction(
|
||||
isDefaultAction: true,
|
||||
child: Text(
|
||||
"Yes".tr,
|
||||
style: const TextStyle(
|
||||
color: CupertinoColors.activeBlue),
|
||||
),
|
||||
onPressed: () async {
|
||||
// Ensure the provider name is not empty
|
||||
if (providerController.text.isNotEmpty) {
|
||||
// Call the function to insert data into the database
|
||||
await assuranceHealthController
|
||||
.addDriverHealthAssurance(
|
||||
healthInsuranceProvider:
|
||||
providerController.text,
|
||||
);
|
||||
|
||||
// Close the dialog and navigate to a success screen or show a snackbar
|
||||
Navigator.of(context).pop();
|
||||
} else {
|
||||
// Show an alert if the provider name is empty
|
||||
showCupertinoDialog(
|
||||
context: context,
|
||||
builder: (_) => CupertinoAlertDialog(
|
||||
title: Text("Error".tr),
|
||||
content: Text(
|
||||
"Do you have a disease for a long time?"
|
||||
.tr),
|
||||
actions: [
|
||||
CupertinoDialogAction(
|
||||
child: Text("OK".tr),
|
||||
onPressed: () =>
|
||||
Navigator.of(context).pop(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
child: Text(
|
||||
"No".tr,
|
||||
style: const TextStyle(
|
||||
color: CupertinoColors.destructiveRed),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context)
|
||||
.pop(); // Just close the dialog
|
||||
// Optionally show feedback if the driver opts out
|
||||
// Get.snackbar(
|
||||
// "Opted out".tr,
|
||||
// "You have chosen not to proceed with health insurance."
|
||||
// .tr,
|
||||
// backgroundColor:
|
||||
// CupertinoColors.systemGrey);
|
||||
mySnackeBarError(
|
||||
"You have chosen not to proceed with health insurance."
|
||||
.tr);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTripCountAvatar(String count) {
|
||||
return Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: const RadialGradient(
|
||||
colors: [
|
||||
Color(0xFF42A5F5),
|
||||
Color(0xFF1976D2),
|
||||
], // Health theme colors
|
||||
center: Alignment.center,
|
||||
radius: 0.8,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: CupertinoColors.black.withOpacity(0.2),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
count,
|
||||
style: const TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: CupertinoColors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
119
lib/views/home/Captin/bottom_bar.dart
Executable file
119
lib/views/home/Captin/bottom_bar.dart
Executable file
@@ -0,0 +1,119 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/constant/colors.dart';
|
||||
|
||||
class BottomBarController extends GetxController {
|
||||
var currentIndex = 0.obs;
|
||||
|
||||
void changePage(int index) {
|
||||
currentIndex.value = index;
|
||||
}
|
||||
}
|
||||
|
||||
class HomeScreen extends StatelessWidget {
|
||||
final BottomBarController controller = Get.put(BottomBarController());
|
||||
|
||||
HomeScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Bottom Bar Example'.tr),
|
||||
),
|
||||
body: Obx(() => IndexedStack(
|
||||
index: controller.currentIndex.value,
|
||||
children: const [
|
||||
HomeView(),
|
||||
ProfileView(),
|
||||
StatisticsView(),
|
||||
WalletView(),
|
||||
],
|
||||
)),
|
||||
bottomNavigationBar: Obx(() => BottomNavigationBar(
|
||||
backgroundColor: Colors.greenAccent,
|
||||
currentIndex: controller.currentIndex.value,
|
||||
onTap: controller.changePage,
|
||||
items: [
|
||||
BottomNavigationBarItem(
|
||||
icon: const Icon(
|
||||
Icons.home,
|
||||
color: AppColor.primaryColor,
|
||||
),
|
||||
label: 'Home'.tr,
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: const Icon(
|
||||
Icons.person,
|
||||
color: AppColor.primaryColor,
|
||||
),
|
||||
label: 'Profile'.tr,
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: const Icon(
|
||||
Icons.bar_chart,
|
||||
color: AppColor.primaryColor,
|
||||
),
|
||||
label: 'Statistics'.tr,
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: const Icon(
|
||||
Icons.account_balance_wallet,
|
||||
color: AppColor.primaryColor,
|
||||
),
|
||||
label: 'Wallet'.tr,
|
||||
),
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HomeView extends StatelessWidget {
|
||||
const HomeView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Map<String, dynamic> data;
|
||||
return const Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Text('Home View'),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ProfileView extends StatelessWidget {
|
||||
const ProfileView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Center(
|
||||
child: Text('Profile View'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class StatisticsView extends StatelessWidget {
|
||||
const StatisticsView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Center(
|
||||
child: Text('Statistics View'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class WalletView extends StatelessWidget {
|
||||
const WalletView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Center(
|
||||
child: Text('Wallet View'),
|
||||
);
|
||||
}
|
||||
}
|
||||
303
lib/views/home/Captin/camer_widget.dart
Executable file
303
lib/views/home/Captin/camer_widget.dart
Executable file
@@ -0,0 +1,303 @@
|
||||
import 'package:camera/camera.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/controller/functions/camer_controller.dart';
|
||||
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
|
||||
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
||||
|
||||
class CameraWidgetCardId extends StatelessWidget {
|
||||
final CameraClassController cameraClassController =
|
||||
Get.put(CameraClassController());
|
||||
|
||||
CameraWidgetCardId({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MyScafolld(
|
||||
title: 'Scan Id'.tr,
|
||||
body: [
|
||||
Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
|
||||
child: GetBuilder<CameraClassController>(
|
||||
builder: (cameraClassController) =>
|
||||
cameraClassController.isCameraInitialized
|
||||
? Stack(
|
||||
children: [
|
||||
Container(
|
||||
decoration: AppStyle.boxDecoration,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.fitWidth,
|
||||
child: SizedBox(
|
||||
width: Get.width * .9,
|
||||
height: Get.width *
|
||||
.9, // Set the desired aspect ratio here
|
||||
child: CameraPreview(
|
||||
cameraClassController.cameraController,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 72,
|
||||
child: Container(
|
||||
width: 230,
|
||||
height: 25,
|
||||
decoration: BoxDecoration(
|
||||
// color: AppColor.blueColor,
|
||||
border: Border.all(
|
||||
color: AppColor.yellowColor,
|
||||
width: 2),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 60,
|
||||
right: 5,
|
||||
child: Container(
|
||||
width: 230,
|
||||
height: 25,
|
||||
decoration: BoxDecoration(
|
||||
// color: AppColor.blueColor,
|
||||
border: Border.all(
|
||||
color: AppColor.blueColor, width: 2),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 156,
|
||||
right: 5,
|
||||
child: Container(
|
||||
width: 140,
|
||||
height: 20,
|
||||
decoration: BoxDecoration(
|
||||
// color: AppColor.blueColor,
|
||||
border: Border.all(
|
||||
color: AppColor.blueColor, width: 2),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 175,
|
||||
right: 5,
|
||||
child: Container(
|
||||
width: 140,
|
||||
height: 15,
|
||||
decoration: BoxDecoration(
|
||||
// color: AppColor.blueColor,
|
||||
border: Border.all(
|
||||
color: AppColor.blueColor, width: 2),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 191,
|
||||
right: 5,
|
||||
child: Container(
|
||||
width: 140,
|
||||
height: 15,
|
||||
decoration: BoxDecoration(
|
||||
// color: AppColor.blueColor,
|
||||
border: Border.all(
|
||||
color: AppColor.blueColor, width: 2),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 207,
|
||||
right: 5,
|
||||
child: Container(
|
||||
width: 140,
|
||||
height: 15,
|
||||
decoration: BoxDecoration(
|
||||
// color: AppColor.blueColor,
|
||||
border: Border.all(
|
||||
color: AppColor.blueColor, width: 2),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 225,
|
||||
right: 5,
|
||||
child: Container(
|
||||
width: 140,
|
||||
height: 15,
|
||||
decoration: BoxDecoration(
|
||||
// color: AppColor.blueColor,
|
||||
border: Border.all(
|
||||
color: AppColor.blueColor, width: 2),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 115,
|
||||
left: 25,
|
||||
child: Container(
|
||||
width: 120,
|
||||
height: 110,
|
||||
decoration: BoxDecoration(
|
||||
// color: AppColor.blueColor,
|
||||
border: Border.all(
|
||||
color: AppColor.blueColor, width: 2),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Container(
|
||||
decoration: AppStyle.boxDecoration,
|
||||
height: Get.width * 3 / 4,
|
||||
width: Get.width,
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Camera not initilaized yet'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
MyElevatedButton(
|
||||
title: 'Scan ID MklGoogle'.tr, onPressed: () {}),
|
||||
// cameraClassController.takePictureAndMLGoogleScan()),
|
||||
MyElevatedButton(
|
||||
title: 'Scan ID Tesseract'.tr, onPressed: () {}),
|
||||
],
|
||||
),
|
||||
MyElevatedButton(
|
||||
title: 'Scan ID Api'.tr,
|
||||
onPressed: () => cameraClassController.extractCardId()),
|
||||
GetBuilder<CameraClassController>(
|
||||
builder: (cameraClassController) => Expanded(
|
||||
child:
|
||||
Text(cameraClassController.scannedText.toString())))
|
||||
],
|
||||
)
|
||||
],
|
||||
isleading: true);
|
||||
}
|
||||
}
|
||||
|
||||
class CameraWidgetPassPort extends StatelessWidget {
|
||||
final CameraClassController cameraClassController =
|
||||
Get.put(CameraClassController());
|
||||
|
||||
CameraWidgetPassPort({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MyScafolld(
|
||||
title: 'Scan Id'.tr,
|
||||
body: [
|
||||
Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
|
||||
child: GetBuilder<CameraClassController>(
|
||||
builder: (cameraClassController) =>
|
||||
cameraClassController.isCameraInitialized
|
||||
? Stack(
|
||||
children: [
|
||||
Container(
|
||||
decoration: AppStyle.boxDecoration,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.fitWidth,
|
||||
child: SizedBox(
|
||||
width: Get.width * .9,
|
||||
height: Get.width *
|
||||
.9, // Set the desired aspect ratio here
|
||||
child: CameraPreview(
|
||||
cameraClassController.cameraController,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 35,
|
||||
left: 35,
|
||||
width: Get.width * .77,
|
||||
height:
|
||||
17, //left":95.0,"top":134.0,"width":2909.0,"height":175.0
|
||||
child: Container(
|
||||
// width: 230,
|
||||
// height: 25,
|
||||
decoration: BoxDecoration(
|
||||
// color: AppColor.blueColor,
|
||||
border: Border.all(
|
||||
color: AppColor.yellowColor,
|
||||
width: 2),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 60,
|
||||
right: 25,
|
||||
width: 90,
|
||||
height: 25,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
// color: AppColor.blueColor,
|
||||
border: Border.all(
|
||||
color: AppColor.blueColor, width: 2),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 110,
|
||||
right: 90,
|
||||
child: Container(
|
||||
width: 140,
|
||||
height: 20,
|
||||
decoration: BoxDecoration(
|
||||
// color: AppColor.blueColor,
|
||||
border: Border.all(
|
||||
color: AppColor.blueColor, width: 2),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Container(
|
||||
decoration: AppStyle.boxDecoration,
|
||||
height: Get.width * 3 / 4,
|
||||
width: Get.width,
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Camera not initilaized yet',
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
MyElevatedButton(
|
||||
title: 'Scan ID MklGoogle'.tr, onPressed: () => {}),
|
||||
// cameraClassController.takePictureAndMLGoogleScan()),
|
||||
MyElevatedButton(
|
||||
title: 'Scan ID Tesseract'.tr, onPressed: () {}),
|
||||
],
|
||||
),
|
||||
MyElevatedButton(
|
||||
title: 'Scan ID Api'.tr,
|
||||
onPressed: () => cameraClassController.extractCardId()),
|
||||
GetBuilder<CameraClassController>(
|
||||
builder: (cameraClassController) => Expanded(
|
||||
child:
|
||||
Text(cameraClassController.scannedText.toString())))
|
||||
],
|
||||
)
|
||||
],
|
||||
isleading: true);
|
||||
}
|
||||
}
|
||||
173
lib/views/home/Captin/driver_map_page.dart
Executable file
173
lib/views/home/Captin/driver_map_page.dart
Executable file
@@ -0,0 +1,173 @@
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart';
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../controller/functions/location_controller.dart';
|
||||
import '../../Rate/rate_passenger.dart';
|
||||
import '../../widgets/my_textField.dart';
|
||||
import 'mapDriverWidgets/driver_end_ride_bar.dart';
|
||||
import 'mapDriverWidgets/google_driver_map_page.dart';
|
||||
import 'mapDriverWidgets/passenger_info_window.dart';
|
||||
import 'mapDriverWidgets/sos_connect.dart';
|
||||
|
||||
class PassengerLocationMapPage extends StatelessWidget {
|
||||
PassengerLocationMapPage({super.key});
|
||||
final LocationController locationController = Get.put(LocationController());
|
||||
final MapDriverController mapDriverController =
|
||||
Get.put(MapDriverController());
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (Get.arguments != null && Get.arguments is Map<String, dynamic>) {
|
||||
// نستخدم addPostFrameCallback لضمان أن هذا الكود يعمل بعد اكتمال بناء الإطار الأول
|
||||
// هذا يعطي GetX وقته لتجهيز كل شيء
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// نستدعي دالة التهيئة الجديدة ونمرر لها البيانات
|
||||
mapDriverController.argumentLoading();
|
||||
});
|
||||
} else {
|
||||
// في حال عدم وجود arguments، يجب التعامل مع هذا الخطأ
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
Get.snackbar("Error", "No order data found.");
|
||||
Get.back();
|
||||
});
|
||||
}
|
||||
mapDriverController.argumentLoading();
|
||||
mapDriverController.startTimerToShowPassengerInfoWindowFromDriver();
|
||||
|
||||
return Scaffold(
|
||||
// backgroundColor: AppColor.blueColor,
|
||||
// title: 'Map Passenger'.tr,
|
||||
body: SafeArea(
|
||||
child: Stack(
|
||||
children: [
|
||||
GoogleDriverMap(locationController: locationController),
|
||||
const PassengerInfoWindow(),
|
||||
CancelWidget(mapDriverController: mapDriverController),
|
||||
driverEndRideBar(),
|
||||
const SosConnect(),
|
||||
speedCircle(),
|
||||
// const GoogleMapApp(),
|
||||
const PricesWindow(),
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
class CancelWidget extends StatelessWidget {
|
||||
const CancelWidget({
|
||||
super.key,
|
||||
required this.mapDriverController,
|
||||
});
|
||||
|
||||
final MapDriverController mapDriverController;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Positioned(
|
||||
top: 10,
|
||||
left: 5,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Get.defaultDialog(
|
||||
title: "Are you sure you want to cancel this trip?".tr,
|
||||
titleStyle: AppStyle.title,
|
||||
content: Column(
|
||||
children: [
|
||||
Text("Why do you want to cancel this trip?".tr),
|
||||
Form(
|
||||
key: mapDriverController.formKeyCancel,
|
||||
child: MyTextForm(
|
||||
controller: mapDriverController.cancelTripCotroller,
|
||||
label: "Write the reason for canceling the trip".tr,
|
||||
hint: "Write the reason for canceling the trip".tr,
|
||||
type: TextInputType.name,
|
||||
))
|
||||
],
|
||||
),
|
||||
confirm: MyElevatedButton(
|
||||
title: 'Ok'.tr,
|
||||
kolor: AppColor.redColor,
|
||||
onPressed: () async {
|
||||
// todo add cancel and inform passenger to get new driver
|
||||
await mapDriverController
|
||||
.cancelTripFromDriverAfterApplied();
|
||||
Get.back();
|
||||
}),
|
||||
cancel: MyElevatedButton(
|
||||
title: 'No'.tr,
|
||||
// kolor: AppColor.redColor,
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
}));
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.redColor,
|
||||
borderRadius: BorderRadius.circular(15)),
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(3),
|
||||
child: Icon(
|
||||
Icons.clear,
|
||||
size: 40,
|
||||
color: AppColor.secondaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PricesWindow extends StatelessWidget {
|
||||
const PricesWindow({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MapDriverController>(builder: (mapDriverController) {
|
||||
return mapDriverController.isPriceWindow
|
||||
? Positioned(
|
||||
bottom: Get.height * 1.2,
|
||||
// top: Get.height * 3,
|
||||
left: Get.height * 1,
|
||||
right: Get.height * 1,
|
||||
child: Container(
|
||||
height: Get.height * 3,
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(3),
|
||||
child: Text(
|
||||
'Total Price is '.tr,
|
||||
style: AppStyle.headTitle2,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
)),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
MyElevatedButton(
|
||||
title: 'ok'.tr,
|
||||
onPressed: () =>
|
||||
Get.to(() => RatePassenger(), arguments: {
|
||||
'rideId': mapDriverController.rideId,
|
||||
'passengerId': mapDriverController.passengerId,
|
||||
'driverId': mapDriverController.driverId
|
||||
}))
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox();
|
||||
});
|
||||
}
|
||||
}
|
||||
149
lib/views/home/Captin/history/history_captain.dart
Executable file
149
lib/views/home/Captin/history/history_captain.dart
Executable file
@@ -0,0 +1,149 @@
|
||||
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../../controller/auth/captin/history_captain.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../../controller/functions/encrypt_decrypt.dart';
|
||||
|
||||
class HistoryCaptain extends StatelessWidget {
|
||||
const HistoryCaptain({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(HistoryCaptainController());
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text('History Page'.tr),
|
||||
leading: CupertinoNavigationBarBackButton(
|
||||
onPressed: () => Get.back(),
|
||||
),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: GetBuilder<HistoryCaptainController>(
|
||||
builder: (historyCaptainController) => historyCaptainController
|
||||
.isloading
|
||||
? const Center(child: CupertinoActivityIndicator())
|
||||
: historyCaptainController.historyData['message'].length < 1
|
||||
? Center(
|
||||
child: Text(
|
||||
'No ride Yet.'.tr,
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.navTitleTextStyle,
|
||||
),
|
||||
)
|
||||
: ListView.builder(
|
||||
itemCount: historyCaptainController
|
||||
.historyData['message'].length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
var list = historyCaptainController
|
||||
.historyData['message'][index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: CupertinoColors.systemGrey, width: 1),
|
||||
borderRadius:
|
||||
const BorderRadius.all(Radius.circular(8.0)),
|
||||
),
|
||||
child: CupertinoButton(
|
||||
onPressed: () {
|
||||
if (list['status'] != 'Cancel') {
|
||||
historyCaptainController
|
||||
.getHistoryDetails(list['order_id']);
|
||||
} else {
|
||||
MyDialog().getDialog(
|
||||
'This Trip Cancelled'.tr,
|
||||
'This Trip Cancelled'.tr,
|
||||
() => Get.back(),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(8),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'OrderId'.tr,
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.navTitleTextStyle,
|
||||
),
|
||||
Text(
|
||||
EncryptionHelper.instance
|
||||
.decryptData(list['order_id']),
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.textStyle,
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'created time'.tr,
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.navTitleTextStyle,
|
||||
),
|
||||
Text(
|
||||
list['created_at'],
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.textStyle,
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
list['status'],
|
||||
style: EncryptionHelper.instance
|
||||
.decryptData(
|
||||
list['status']) ==
|
||||
'Apply'
|
||||
? CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.navTitleTextStyle
|
||||
.copyWith(
|
||||
color: CupertinoColors
|
||||
.systemGreen)
|
||||
: EncryptionHelper.instance.decryptData(
|
||||
list['status']) ==
|
||||
'Refused'
|
||||
? CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.navTitleTextStyle
|
||||
.copyWith(
|
||||
color: CupertinoColors
|
||||
.systemRed)
|
||||
: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.navTitleTextStyle
|
||||
.copyWith(
|
||||
color: CupertinoColors
|
||||
.systemYellow),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
252
lib/views/home/Captin/history/history_details_page.dart
Executable file
252
lib/views/home/Captin/history/history_details_page.dart
Executable file
@@ -0,0 +1,252 @@
|
||||
import 'package:sefer_driver/controller/functions/location_controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/controller/auth/captin/history_captain.dart';
|
||||
import 'package:sefer_driver/controller/functions/launch.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
import '../../../../controller/functions/encrypt_decrypt.dart';
|
||||
|
||||
class HistoryDetailsPage extends StatelessWidget {
|
||||
HistoryDetailsPage({super.key});
|
||||
HistoryCaptainController historyCaptainController =
|
||||
Get.put(HistoryCaptainController());
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text('Trip Detail'.tr),
|
||||
leading: CupertinoButton(
|
||||
padding: EdgeInsets.zero,
|
||||
child: const Icon(CupertinoIcons.back),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
child: GetBuilder<HistoryCaptainController>(
|
||||
builder: (historyCaptainController) {
|
||||
var res = historyCaptainController.historyDetailsData['data'];
|
||||
return historyCaptainController.isloading
|
||||
? const Center(
|
||||
child: CupertinoActivityIndicator(),
|
||||
)
|
||||
: CupertinoScrollbar(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
CupertinoButton(
|
||||
onPressed: () {
|
||||
String mapUrl =
|
||||
'https://www.google.com/maps/dir/${EncryptionHelper.instance.decryptData(res['start_location'])}/${EncryptionHelper.instance.decryptData(res['end_location'])}/';
|
||||
showInBrowser(mapUrl);
|
||||
},
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width * 0.9,
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
border: Border.all(
|
||||
color: CupertinoColors.activeBlue,
|
||||
width: 2),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).size.height *
|
||||
0.3,
|
||||
child: GoogleMap(
|
||||
initialCameraPosition: CameraPosition(
|
||||
target: Get.find<LocationController>()
|
||||
.myLocation,
|
||||
tilt: 80,
|
||||
zoom: 13,
|
||||
),
|
||||
zoomControlsEnabled: true,
|
||||
polylines: {
|
||||
Polyline(
|
||||
polylineId: const PolylineId('route'),
|
||||
points: [
|
||||
LatLng(
|
||||
double.parse(EncryptionHelper
|
||||
.instance
|
||||
.decryptData(
|
||||
res['start_location'])
|
||||
.toString()
|
||||
.split(',')[0]),
|
||||
double.parse(EncryptionHelper
|
||||
.instance
|
||||
.decryptData(
|
||||
res['start_location'])
|
||||
.toString()
|
||||
.split(',')[1]),
|
||||
),
|
||||
LatLng(
|
||||
double.parse(EncryptionHelper
|
||||
.instance
|
||||
.decryptData(
|
||||
res['end_location'])
|
||||
.toString()
|
||||
.split(',')[0]),
|
||||
double.parse(EncryptionHelper
|
||||
.instance
|
||||
.decryptData(
|
||||
res['end_location'])
|
||||
.toString()
|
||||
.split(',')[1]),
|
||||
)
|
||||
],
|
||||
color: CupertinoColors.activeGreen,
|
||||
width: 5,
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'${'Order ID'.tr} ${EncryptionHelper.instance.decryptData(res['id'])}',
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.navActionTextStyle,
|
||||
),
|
||||
Text(
|
||||
res['date'].toString(),
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.navActionTextStyle,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width * 0.9,
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
border: Border.all(
|
||||
color: CupertinoColors.activeGreen, width: 2),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'${'Price is'.tr} ${EncryptionHelper.instance.decryptData(res['price_for_driver'])}',
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.textStyle,
|
||||
),
|
||||
Text(
|
||||
'${'Distance is'.tr} ${EncryptionHelper.instance.decryptData(res['distance'])} KM',
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.textStyle,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
'Times of Trip'.tr,
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.navTitleTextStyle,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width * 0.9,
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
border: Border.all(
|
||||
color: CupertinoColors.destructiveRed,
|
||||
width: 2),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
'${'Time to Passenger is'.tr} ${res['DriverIsGoingToPassenger']}',
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.textStyle,
|
||||
),
|
||||
Text(
|
||||
'${'TimeStart is'.tr} ${res['rideTimeStart']}',
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.textStyle,
|
||||
),
|
||||
Text(
|
||||
'${'Time Finish is'.tr} ${res['rideTimeFinish']}',
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.textStyle,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width * 0.9,
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
border: Border.all(
|
||||
color: CupertinoColors.systemGreen, width: 2),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'${'Passenger Name is'.tr} ${EncryptionHelper.instance.decryptData(res['first_name'])} ${EncryptionHelper.instance.decryptData(res['last_name'])}',
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.textStyle,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width * 0.9,
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
border: Border.all(
|
||||
color: CupertinoColors.systemYellow,
|
||||
width: 2),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'${'Status is'.tr} ${EncryptionHelper.instance.decryptData(res['status'])}',
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.textStyle,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
589
lib/views/home/Captin/home_captain/drawer_captain.dart
Executable file
589
lib/views/home/Captin/home_captain/drawer_captain.dart
Executable file
@@ -0,0 +1,589 @@
|
||||
import 'package:sefer_driver/constant/links.dart';
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart';
|
||||
import 'package:sefer_driver/views/Rate/rate_app_page.dart';
|
||||
import 'package:sefer_driver/views/auth/captin/contact_us_page.dart';
|
||||
import 'package:sefer_driver/views/auth/captin/invite_driver_screen.dart';
|
||||
import 'package:sefer_driver/views/notification/available_rides_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/constant/box_name.dart';
|
||||
import 'package:sefer_driver/main.dart';
|
||||
import 'package:sefer_driver/views/auth/captin/logout_captain.dart';
|
||||
import 'package:sefer_driver/views/home/Captin/history/history_captain.dart';
|
||||
import 'package:sefer_driver/views/home/Captin/home_captain/help_captain.dart';
|
||||
import 'package:sefer_driver/views/home/Captin/About%20Us/settings_captain.dart';
|
||||
import 'package:sefer_driver/views/home/my_wallet/walet_captain.dart';
|
||||
import 'package:sefer_driver/views/home/profile/profile_captain.dart';
|
||||
import 'package:sefer_driver/views/notification/notification_captain.dart';
|
||||
|
||||
import '../../../../constant/colors.dart';
|
||||
import '../../../../controller/functions/upload_image.dart';
|
||||
import '../About Us/video_page.dart';
|
||||
import '../assurance_health_page.dart';
|
||||
import '../maintain_center_page.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
// class CupertinoDrawerCaptain extends StatelessWidget {
|
||||
// final ImageController imageController = Get.put(ImageController());
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return CupertinoPageScaffold(
|
||||
// navigationBar: CupertinoNavigationBar(
|
||||
// middle: Text('Menu'.tr),
|
||||
// ),
|
||||
// child: SafeArea(
|
||||
// child: CustomScrollView(
|
||||
// slivers: [
|
||||
// const SliverToBoxAdapter(child: const UserAccountHeader()),
|
||||
// SliverList(
|
||||
// delegate: SliverChildListDelegate([
|
||||
// _buildDivider(),
|
||||
// _buildDrawerItem(
|
||||
// icon: CupertinoIcons.money_dollar,
|
||||
// text: 'Wallet'.tr,
|
||||
// onTap: () => Get.to(() => WalletCaptain()),
|
||||
// ),
|
||||
// _buildDivider(),
|
||||
// _buildDrawerItem(
|
||||
// icon: CupertinoIcons.person,
|
||||
// text: 'Profile'.tr,
|
||||
// onTap: () => Get.to(() => ProfileCaptain()),
|
||||
// ),
|
||||
// _buildDivider(),
|
||||
// _buildDrawerItem(
|
||||
// icon: CupertinoIcons.clock,
|
||||
// text: 'History of Trip'.tr,
|
||||
// onTap: () => Get.to(() => const HistoryCaptain()),
|
||||
// ),
|
||||
// _buildDivider(),
|
||||
// _buildDrawerItem(
|
||||
// icon: CupertinoIcons.car_detailed,
|
||||
// text: 'Available for rides'.tr,
|
||||
// onTap: () => Get.to(() => const AvailableRidesPage()),
|
||||
// ),
|
||||
// _buildDivider(),
|
||||
// _buildDrawerItem(
|
||||
// icon: CupertinoIcons.bell,
|
||||
// text: 'Notifications'.tr,
|
||||
// onTap: () => Get.to(() => const NotificationCaptain()),
|
||||
// ),
|
||||
// _buildDivider(),
|
||||
// _buildDrawerItem(
|
||||
// icon: CupertinoIcons.question_circle,
|
||||
// text: 'Helping Center'.tr,
|
||||
// onTap: () => Get.to(() => HelpCaptain()),
|
||||
// ),
|
||||
// _buildDivider(),
|
||||
// _buildDrawerItem(
|
||||
// icon: CupertinoIcons.share,
|
||||
// text: 'Share App'.tr,
|
||||
// onTap: () => Get.to(() => InviteScreen()),
|
||||
// ),
|
||||
// _buildDivider(),
|
||||
// _buildDrawerItem(
|
||||
// icon: CupertinoIcons.wrench,
|
||||
// text: "Maintenance Center".tr,
|
||||
// onTap: () => Get.to(() => MaintainCenterPage()),
|
||||
// ),
|
||||
// _buildDivider(),
|
||||
// _buildDrawerItem(
|
||||
// icon:
|
||||
// CupertinoIcons.heart, // Updated icon to represent health
|
||||
// text: "Health Insurance".tr, // Updated English text
|
||||
// onTap: () => Get.to(() => AssuranceHealthPage()),
|
||||
// ),
|
||||
// _buildDivider(),
|
||||
// _buildDrawerItem(
|
||||
// icon: CupertinoIcons.mail,
|
||||
// text: "Contact Us".tr,
|
||||
// onTap: () => Get.to(() => ContactUsPage()),
|
||||
// ),
|
||||
// _buildDivider(),
|
||||
// _buildDrawerItem(
|
||||
// icon:
|
||||
// Icons.play_circle_filled, // Icon representing video play
|
||||
// text: 'Videos Tutorials'.tr,
|
||||
// onTap: () => Get.to(() => VideoListPage()),
|
||||
// ),
|
||||
// _buildDivider(),
|
||||
// _buildDrawerItem(
|
||||
// icon: Icons.star, // Another option with a filled star icon
|
||||
// text: "Rate Our App".tr,
|
||||
// onTap: () => Get.to(() => RatingScreen()),
|
||||
// ),
|
||||
// _buildDivider(),
|
||||
// _buildDrawerItem(
|
||||
// icon: CupertinoIcons.settings,
|
||||
// text: 'Settings'.tr,
|
||||
// onTap: () => Get.to(() => const SettingsCaptain()),
|
||||
// ),
|
||||
// _buildDivider(),
|
||||
// _buildDrawerItem(
|
||||
// icon: CupertinoIcons.square_arrow_right,
|
||||
// text: 'Sign Out'.tr,
|
||||
// onTap: () => Get.to(() => const LogoutCaptain()),
|
||||
// ),
|
||||
// _buildDivider(),
|
||||
// ]),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
// Widget _buildDivider() {
|
||||
// return const Divider(
|
||||
// thickness: 1,
|
||||
// height: 1,
|
||||
// color: CupertinoColors.systemGrey4,
|
||||
// );
|
||||
// }
|
||||
|
||||
// Widget _buildDrawerItem({
|
||||
// required IconData icon,
|
||||
// required String text,
|
||||
// required VoidCallback onTap,
|
||||
// }) {
|
||||
// return CupertinoButton(
|
||||
// onPressed: onTap,
|
||||
// child: Row(
|
||||
// children: [
|
||||
// Icon(icon, color: CupertinoColors.activeBlue),
|
||||
// const SizedBox(width: 10),
|
||||
// Text(text, style: const TextStyle(color: CupertinoColors.label)),
|
||||
// const Spacer(),
|
||||
// const Icon(CupertinoIcons.right_chevron,
|
||||
// color: CupertinoColors.systemGrey),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
// class UserAccountHeader extends StatelessWidget {
|
||||
// const UserAccountHeader({super.key});
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Container(
|
||||
// padding: const EdgeInsets.all(16),
|
||||
// decoration: const BoxDecoration(
|
||||
// gradient: LinearGradient(
|
||||
// colors: [AppColor.blueColor, AppColor.twitterColor],
|
||||
// begin: Alignment.topLeft,
|
||||
// end: Alignment.bottomRight,
|
||||
// ),
|
||||
// ),
|
||||
// child: Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// GetBuilder<ImageController>(
|
||||
// builder: (imageController) {
|
||||
// return Stack(
|
||||
// children: [
|
||||
// imageController.isloading
|
||||
// ? const CupertinoActivityIndicator()
|
||||
// : Container(
|
||||
// width: 100,
|
||||
// height: 100,
|
||||
// decoration: BoxDecoration(
|
||||
// shape: BoxShape.circle,
|
||||
// image: DecorationImage(
|
||||
// fit: BoxFit.cover,
|
||||
// image: NetworkImage(
|
||||
// '${AppLink.seferCairoServer}/portrate_captain_image/${box.read(BoxName.driverID)}.jpg',
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Positioned(
|
||||
// right: 0,
|
||||
// top: 0,
|
||||
// child: CupertinoButton(
|
||||
// onPressed: () {
|
||||
// imageController.choosImagePicture(
|
||||
// AppLink.uploadImage1, 'portrait');
|
||||
// },
|
||||
// child: const Icon(CupertinoIcons.pencil_circle_fill,
|
||||
// color: CupertinoColors.white),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// const SizedBox(height: 10),
|
||||
// Text(
|
||||
// '${box.read(BoxName.nameDriver).toString().split(' ')[0]} ${box.read(BoxName.nameDriver).toString().split(' ')[1]}',
|
||||
// style: const TextStyle(
|
||||
// color: CupertinoColors.white,
|
||||
// fontSize: 18,
|
||||
// fontWeight: FontWeight.bold),
|
||||
// ),
|
||||
// const SizedBox(height: 5),
|
||||
// Text(
|
||||
// box.read(BoxName.emailDriver),
|
||||
// style: const TextStyle(color: CupertinoColors.white, fontSize: 14),
|
||||
// ),
|
||||
// const SizedBox(height: 10),
|
||||
// Row(
|
||||
// children: [
|
||||
// Text(
|
||||
// Get.find<HomeCaptainController>().rating.toString(),
|
||||
// style: const TextStyle(
|
||||
// color: CupertinoColors.systemYellow, fontSize: 16),
|
||||
// ),
|
||||
// const SizedBox(width: 5),
|
||||
// // You might want to replace this with a Cupertino-style rating widget
|
||||
// // For now, we'll use text to represent stars
|
||||
// // const Text('★★★★★',
|
||||
// // style: TextStyle(color: CupertinoColors.systemYellow)),
|
||||
|
||||
// Container(
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
|
||||
// color: AppColor.greenColor,
|
||||
// child: RatingBar.builder(
|
||||
// initialRating: double.parse(
|
||||
// Get.find<HomeCaptainController>().rating.toString()),
|
||||
// minRating: 1,
|
||||
// direction: Axis.horizontal,
|
||||
// itemCount: 5,
|
||||
// itemSize: 20,
|
||||
// itemPadding: const EdgeInsets.symmetric(horizontal: 2),
|
||||
// itemBuilder: (context, _) => const Icon(
|
||||
// Icons.star,
|
||||
// color: Colors.amber,
|
||||
// ),
|
||||
// onRatingUpdate: (rating) {},
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
class CupertinoDrawerCaptain extends StatelessWidget {
|
||||
final ImageController imageController = Get.put(ImageController());
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CupertinoPageScaffold(
|
||||
backgroundColor: CupertinoColors.systemGroupedBackground,
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text('Menu'.tr,
|
||||
style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w600)),
|
||||
backgroundColor: Colors.transparent,
|
||||
border: null,
|
||||
),
|
||||
child: SafeArea(
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
const SliverToBoxAdapter(child: UserAccountHeader()),
|
||||
SliverList(
|
||||
delegate: SliverChildListDelegate([
|
||||
const SizedBox(height: 10),
|
||||
_buildSectionHeader('Account'.tr),
|
||||
_buildDrawerItem(
|
||||
icon: CupertinoIcons.money_dollar_circle_fill,
|
||||
text: 'Wallet'.tr,
|
||||
onTap: () => Get.to(() => WalletCaptainRefactored()),
|
||||
),
|
||||
_buildDrawerItem(
|
||||
icon: CupertinoIcons.person_fill,
|
||||
text: 'Profile'.tr,
|
||||
onTap: () => Get.to(() => ProfileCaptain()),
|
||||
),
|
||||
_buildSectionHeader('Activities'.tr),
|
||||
_buildDrawerItem(
|
||||
icon: CupertinoIcons.clock_fill,
|
||||
text: 'History of Trip'.tr,
|
||||
onTap: () => Get.to(() => const HistoryCaptain()),
|
||||
),
|
||||
_buildDrawerItem(
|
||||
icon: CupertinoIcons.car_fill,
|
||||
text: 'Available for rides'.tr,
|
||||
onTap: () => Get.to(() => const AvailableRidesPage()),
|
||||
),
|
||||
_buildSectionHeader('Support'.tr),
|
||||
_buildDrawerItem(
|
||||
icon: CupertinoIcons.bell_fill,
|
||||
text: 'Notifications'.tr,
|
||||
onTap: () => Get.to(() => const NotificationCaptain()),
|
||||
),
|
||||
_buildDrawerItem(
|
||||
icon: CupertinoIcons.question_circle_fill,
|
||||
text: 'Helping Center'.tr,
|
||||
onTap: () => Get.to(() => HelpCaptain()),
|
||||
),
|
||||
_buildSectionHeader('More'.tr),
|
||||
...moreMenuItems(),
|
||||
]),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> moreMenuItems() => [
|
||||
_buildDrawerItem(
|
||||
icon: CupertinoIcons.share_up,
|
||||
text: 'Share App'.tr,
|
||||
onTap: () => Get.to(() => InviteScreen()),
|
||||
),
|
||||
_buildDrawerItem(
|
||||
icon: CupertinoIcons.wrench_fill,
|
||||
text: "Maintenance Center".tr,
|
||||
onTap: () => Get.to(() => MaintainCenterPage()),
|
||||
),
|
||||
_buildDrawerItem(
|
||||
icon: CupertinoIcons.heart_fill,
|
||||
text: "Health Insurance".tr,
|
||||
onTap: () => Get.to(() => AssuranceHealthPage()),
|
||||
),
|
||||
_buildDrawerItem(
|
||||
icon: CupertinoIcons.mail_solid,
|
||||
text: "Contact Us".tr,
|
||||
onTap: () => Get.to(() => ContactUsPage()),
|
||||
),
|
||||
_buildDrawerItem(
|
||||
icon: CupertinoIcons.play_circle_fill,
|
||||
text: 'Videos Tutorials'.tr,
|
||||
onTap: () => Get.to(() => VideoListPage()),
|
||||
),
|
||||
_buildDrawerItem(
|
||||
icon: CupertinoIcons.star_fill,
|
||||
text: "Rate Our App".tr,
|
||||
onTap: () => Get.to(() => RatingScreen()),
|
||||
),
|
||||
_buildDrawerItem(
|
||||
icon: CupertinoIcons.settings_solid,
|
||||
text: 'Settings'.tr,
|
||||
onTap: () => Get.to(() => const SettingsCaptain()),
|
||||
),
|
||||
_buildDrawerItem(
|
||||
icon: CupertinoIcons.square_arrow_right,
|
||||
text: 'Sign Out'.tr,
|
||||
onTap: () => Get.to(() => const LogoutCaptain()),
|
||||
isDestructive: true,
|
||||
),
|
||||
];
|
||||
|
||||
Widget _buildSectionHeader(String title) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
color: CupertinoColors.systemGrey,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDrawerItem({
|
||||
required IconData icon,
|
||||
required String text,
|
||||
required VoidCallback onTap,
|
||||
bool isDestructive = false,
|
||||
}) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: CupertinoColors.white,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: CupertinoButton(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
onPressed: onTap,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
color: isDestructive
|
||||
? CupertinoColors.destructiveRed
|
||||
: CupertinoColors.activeBlue,
|
||||
size: 22,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
text,
|
||||
style: AppStyle.title,
|
||||
// TextStyle(
|
||||
// color: isDestructive
|
||||
// ? CupertinoColors.destructiveRed
|
||||
// : CupertinoColors.label,
|
||||
// fontSize: 16,
|
||||
// ),
|
||||
),
|
||||
const Spacer(),
|
||||
Icon(
|
||||
CupertinoIcons.chevron_right,
|
||||
color: CupertinoColors.systemGrey3,
|
||||
size: 18,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UserAccountHeader extends StatelessWidget {
|
||||
const UserAccountHeader({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.fromLTRB(20, 20, 20, 24),
|
||||
decoration: BoxDecoration(
|
||||
gradient: const LinearGradient(
|
||||
colors: [AppColor.blueColor, Color(0xFF2196F3)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withAlpha(10),
|
||||
blurRadius: 10,
|
||||
spreadRadius: 2,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
GetBuilder<ImageController>(
|
||||
builder: (imageController) {
|
||||
return Stack(
|
||||
children: [
|
||||
Container(
|
||||
width: 100,
|
||||
height: 100,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(color: Colors.white, width: 3),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.2),
|
||||
blurRadius: 8,
|
||||
spreadRadius: 2,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: imageController.isloading
|
||||
? const CupertinoActivityIndicator()
|
||||
: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
child: Image.network(
|
||||
'${AppLink.seferCairoServer}/portrate_captain_image/${(box.read(BoxName.driverID))}.jpg',
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: CupertinoButton(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: () {
|
||||
imageController.choosImagePicture(
|
||||
AppLink.uploadImagePortrate, 'portrait');
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(6),
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
CupertinoIcons.camera_fill,
|
||||
color: AppColor.blueColor,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
box.read(BoxName.nameDriver).toString(),
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
shadows: [
|
||||
Shadow(
|
||||
color: Colors.black26,
|
||||
blurRadius: 2,
|
||||
offset: Offset(0, 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
box.read(BoxName.emailDriver),
|
||||
style: TextStyle(
|
||||
color: Colors.white.withOpacity(0.9),
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.15),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// const Icon(
|
||||
// CupertinoIcons.star_fill,
|
||||
// color: CupertinoColors.systemYellow,
|
||||
// size: 18,
|
||||
// ),
|
||||
// const SizedBox(width: 4),
|
||||
Text(
|
||||
Get.find<HomeCaptainController>().rating.toString(),
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
RatingBar.builder(
|
||||
initialRating: double.parse(
|
||||
Get.find<HomeCaptainController>().rating.toString()),
|
||||
minRating: 1,
|
||||
direction: Axis.horizontal,
|
||||
itemCount: 5,
|
||||
itemSize: 16,
|
||||
ignoreGestures: true,
|
||||
itemBuilder: (context, _) => const Icon(
|
||||
CupertinoIcons.star_fill,
|
||||
color: CupertinoColors.systemYellow,
|
||||
),
|
||||
onRatingUpdate: (rating) {},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
189
lib/views/home/Captin/home_captain/driver_call_page.dart
Executable file
189
lib/views/home/Captin/home_captain/driver_call_page.dart
Executable file
@@ -0,0 +1,189 @@
|
||||
// import 'dart:async';
|
||||
// import 'package:SEFER/constant/box_name.dart';
|
||||
// import 'package:SEFER/main.dart';
|
||||
// import 'package:SEFER/views/widgets/my_scafold.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:get/get.dart';
|
||||
// import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
// import 'package:agora_rtc_engine/agora_rtc_engine.dart';
|
||||
|
||||
// import '../../../../constant/api_key.dart';
|
||||
// import '../../../../controller/functions/crud.dart';
|
||||
|
||||
// String appId = AK.agoraAppId;
|
||||
|
||||
// class DriverCallPage extends StatefulWidget {
|
||||
// const DriverCallPage({super.key});
|
||||
|
||||
// @override
|
||||
// State<DriverCallPage> createState() => _DriverCallPageState();
|
||||
// }
|
||||
|
||||
// class _DriverCallPageState extends State<DriverCallPage> {
|
||||
// String channelName = '';
|
||||
// String token = '';
|
||||
// // "00612994c6e707543e68d5638894d04f989IAAlydoFEC3ZeZkeUwl0dSswZTX8n+xyZR8PBWdwXFV6t6MLiA8AAAAAEACCHD/gn3TUZQEAAQAAAAAA";
|
||||
|
||||
// // int uid = int.parse(box.read(BoxName.phoneDriver)); // uid of the local user
|
||||
// int uid = 0;
|
||||
// int? _remoteUid; // uid of the remote user
|
||||
// bool _isJoined = false; // Indicates if the local user has joined the channel
|
||||
// late RtcEngine agoraEngine; // Agora engine instance
|
||||
|
||||
// final GlobalKey<ScaffoldMessengerState> scaffoldMessengerKey =
|
||||
// GlobalKey<ScaffoldMessengerState>(); // Global key to access the scaffold
|
||||
|
||||
// showMessage(String message) {
|
||||
// scaffoldMessengerKey.currentState?.showSnackBar(SnackBar(
|
||||
// content: Text(message),
|
||||
// ));
|
||||
// }
|
||||
|
||||
// initAgora() async {
|
||||
// await fetchToken();
|
||||
// await setupVoiceSDKEngine();
|
||||
// }
|
||||
|
||||
// fetchToken() async {
|
||||
// var res = await CRUD()
|
||||
// .getAgoraToken(channelName: channelName, uid: uid.toString());
|
||||
// setState(() {
|
||||
// token = res;
|
||||
// });
|
||||
// }
|
||||
|
||||
// @override
|
||||
// void initState() {
|
||||
// super.initState();
|
||||
// _remoteUid = box.read(BoxName.phone) != null
|
||||
// ? int.parse(box.read(BoxName.phone))
|
||||
// : int.parse(box.read(BoxName.phoneDriver));
|
||||
// uid = box.read(BoxName.phoneDriver) != null
|
||||
// ? int.parse(box.read(BoxName.phoneDriver))
|
||||
// : int.parse(box.read(BoxName.phone));
|
||||
// // Set up an instance of Agora engine
|
||||
// initAgora();
|
||||
// }
|
||||
|
||||
// Future<void> setupVoiceSDKEngine() async {
|
||||
// // retrieve or request microphone permission
|
||||
// await [Permission.microphone].request();
|
||||
|
||||
// //create an instance of the Agora engine
|
||||
// agoraEngine = createAgoraRtcEngine();
|
||||
// await agoraEngine.initialize(RtcEngineContext(appId: AK.agoraAppId));
|
||||
// // Register the event handler
|
||||
// agoraEngine.registerEventHandler(
|
||||
// RtcEngineEventHandler(
|
||||
// onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
|
||||
// showMessage(
|
||||
// "Local user uid:${connection.localUid} joined the channel");
|
||||
// setState(() {
|
||||
// _isJoined = true;
|
||||
// });
|
||||
// },
|
||||
// onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) {
|
||||
// showMessage("Remote user uid:$remoteUid joined the channel");
|
||||
// setState(() {
|
||||
// _remoteUid = remoteUid;
|
||||
// });
|
||||
// },
|
||||
// onUserOffline: (RtcConnection connection, int remoteUid,
|
||||
// UserOfflineReasonType reason) {
|
||||
// showMessage("Remote user uid:$remoteUid left the channel");
|
||||
// setState(() {
|
||||
// _remoteUid = null;
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
// void join() async {
|
||||
// // Set channel options including the client role and channel profile
|
||||
// ChannelMediaOptions options = const ChannelMediaOptions(
|
||||
// clientRoleType: ClientRoleType.clientRoleBroadcaster,
|
||||
// channelProfile: ChannelProfileType.channelProfileCommunication,
|
||||
// );
|
||||
|
||||
// await agoraEngine.joinChannel(
|
||||
// token: token,
|
||||
// channelId: channelName,
|
||||
// options: options,
|
||||
// uid: uid,
|
||||
// );
|
||||
// }
|
||||
// //https://console.agora.io/invite?sign=5e9e22d06f22caeeada9954c9e908572%253A5ba8aed978a35eab5a5113742502ded2a41478b2a81cb19c71a30776e125b58a
|
||||
|
||||
// void leave() {
|
||||
// setState(() {
|
||||
// _isJoined = false;
|
||||
// _remoteUid = null;
|
||||
// });
|
||||
// agoraEngine.leaveChannel();
|
||||
// }
|
||||
|
||||
// // Clean up the resources when you leave
|
||||
// @override
|
||||
// void dispose() async {
|
||||
// await agoraEngine.leaveChannel();
|
||||
// super.dispose();
|
||||
// }
|
||||
|
||||
// // Build UI
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return MaterialApp(
|
||||
// scaffoldMessengerKey: scaffoldMessengerKey,
|
||||
// home: MyScafolld(
|
||||
// // appBar: AppBar(
|
||||
// // title: const Text('Get started with Voice Calling'),
|
||||
// // ),
|
||||
// title: 'Voice Calling'.tr,
|
||||
// isleading: true,
|
||||
// body: [
|
||||
// ListView(
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
// children: [
|
||||
// // Status text
|
||||
// Container(height: 40, child: Center(child: _status())),
|
||||
// // Button Row
|
||||
// Row(
|
||||
// children: <Widget>[
|
||||
// Expanded(
|
||||
// child: ElevatedButton(
|
||||
// child: const Text("Join"),
|
||||
// onPressed: () => {join()},
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(width: 10),
|
||||
// Expanded(
|
||||
// child: ElevatedButton(
|
||||
// child: const Text("Leave"),
|
||||
// onPressed: () => {leave()},
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ]),
|
||||
// );
|
||||
// }
|
||||
|
||||
// Widget _status() {
|
||||
// String statusText;
|
||||
|
||||
// if (!_isJoined)
|
||||
// statusText = 'Join a channel';
|
||||
// else if (_remoteUid == null)
|
||||
// statusText = 'Waiting for a remote user to join...';
|
||||
// else
|
||||
// statusText = 'Connected to remote user, uid:$_remoteUid';
|
||||
|
||||
// return Text(
|
||||
// statusText,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
333
lib/views/home/Captin/home_captain/help_captain.dart
Executable file
333
lib/views/home/Captin/home_captain/help_captain.dart
Executable file
@@ -0,0 +1,333 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/controller/home/captin/help/help_controller.dart';
|
||||
import 'package:sefer_driver/views/home/Captin/home_captain/help_details_replay_page.dart';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
import '../../../../controller/functions/encrypt_decrypt.dart';
|
||||
|
||||
class HelpCaptain extends StatelessWidget {
|
||||
HelpCaptain({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(HelpController());
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text(
|
||||
'Helping Page'.tr,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
leading: CupertinoButton(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Icon(CupertinoIcons.back),
|
||||
),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(
|
||||
20.0), // Increased padding around the content
|
||||
child: ListView(
|
||||
// crossAxisAlignment:
|
||||
// CrossAxisAlignment.stretch, // Stretch children to full width
|
||||
children: [
|
||||
Container(
|
||||
padding:
|
||||
const EdgeInsets.all(18.0), // Slightly increased padding
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
CupertinoTheme.of(context).brightness == Brightness.light
|
||||
? CupertinoColors.systemGrey6
|
||||
: CupertinoColors.darkBackgroundGray,
|
||||
borderRadius:
|
||||
BorderRadius.circular(15.0), // More rounded corners
|
||||
),
|
||||
child: Text(
|
||||
'If you need any help or have questions, this is the right place to do that. You are welcome!'
|
||||
.tr,
|
||||
style:
|
||||
CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 16, // Slightly larger font size
|
||||
color: CupertinoColors.label.resolveFrom(
|
||||
context), // Ensure text color adapts to theme
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
CupertinoFormSection.insetGrouped(
|
||||
// Using CupertinoFormSection for better styling
|
||||
header: Text('Submit Your Question'.tr),
|
||||
margin: EdgeInsets.zero,
|
||||
children: [
|
||||
GetBuilder<HelpController>(
|
||||
builder: (helpController) => Form(
|
||||
key: helpController.formKey,
|
||||
child: CupertinoTextFormFieldRow(
|
||||
controller: helpController.helpQuestionController,
|
||||
placeholder: 'Enter your Question here'.tr,
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Please enter your question'.tr;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
prefix: const Icon(CupertinoIcons
|
||||
.question_circle), // Added a prefix icon
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0, vertical: 10.0),
|
||||
child: GetBuilder<HelpController>(
|
||||
builder: (helpController) => helpController.isLoading
|
||||
? const CupertinoActivityIndicator()
|
||||
: CupertinoButton.filled(
|
||||
onPressed: () {
|
||||
if (helpController.formKey.currentState!
|
||||
.validate()) {
|
||||
helpController.addHelpQuestion();
|
||||
helpController.helpQuestionController
|
||||
.clear(); // Clear the text field
|
||||
}
|
||||
},
|
||||
child: Text('Submit Question'.tr),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
'Your Questions'.tr,
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.navTitleTextStyle
|
||||
.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Expanded(
|
||||
child: GetBuilder<HelpController>(
|
||||
builder: (helpController) =>
|
||||
CupertinoListSection.insetGrouped(
|
||||
margin: EdgeInsets.zero,
|
||||
children: helpController.helpQuestionDate['message'] != null
|
||||
? List.generate(
|
||||
helpController.helpQuestionDate['message'].length,
|
||||
(index) {
|
||||
var list = helpController
|
||||
.helpQuestionDate['message'][index];
|
||||
return CupertinoListTile(
|
||||
title: Text(
|
||||
EncryptionHelper.instance
|
||||
.decryptData(list['helpQuestion']),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
list['datecreated'],
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.tabLabelTextStyle,
|
||||
),
|
||||
const Icon(CupertinoIcons.chevron_forward),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
helpController.getIndex(
|
||||
int.parse(EncryptionHelper.instance
|
||||
.decryptData(list['id'])),
|
||||
EncryptionHelper.instance
|
||||
.decryptData(list['helpQuestion']));
|
||||
helpController
|
||||
.getHelpRepley(list['id'].toString());
|
||||
Get.to(() => const HelpDetailsReplayPage());
|
||||
},
|
||||
);
|
||||
},
|
||||
)
|
||||
: [
|
||||
Center(
|
||||
child: Text('No questions asked yet.'.tr),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
// class HelpCaptain extends StatelessWidget {
|
||||
// HelpCaptain({super.key});
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// Get.put(HelpController());
|
||||
// return CupertinoPageScaffold(
|
||||
// navigationBar: CupertinoNavigationBar(
|
||||
// middle: Text('Helping Page'.tr),
|
||||
// leading: CupertinoButton(
|
||||
// padding: EdgeInsets.zero,
|
||||
// child: Icon(CupertinoIcons.back),
|
||||
// onPressed: () => Navigator.pop(context),
|
||||
// ),
|
||||
// ),
|
||||
// child: SafeArea(
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.all(16.0),
|
||||
// child: Column(
|
||||
// children: [
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.symmetric(vertical: 12.0),
|
||||
// child: Container(
|
||||
// padding: const EdgeInsets.all(16.0),
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius: BorderRadius.circular(12.0),
|
||||
// border: Border.all(
|
||||
// color: CupertinoColors.systemGrey2,
|
||||
// ),
|
||||
// ),
|
||||
// child: Text(
|
||||
// 'If you need any help or have questions, this is the right place to do that. You are welcome!'
|
||||
// .tr,
|
||||
// style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
// textAlign: TextAlign.center,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Card(
|
||||
// elevation: 3,
|
||||
// color: CupertinoColors.systemGrey6,
|
||||
// shape: RoundedRectangleBorder(
|
||||
// borderRadius: BorderRadius.circular(12.0),
|
||||
// ),
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.all(16.0),
|
||||
// child: GetBuilder<HelpController>(
|
||||
// builder: (helpController) => Form(
|
||||
// key: helpController.formKey,
|
||||
// child: Column(
|
||||
// children: [
|
||||
// CupertinoTextField(
|
||||
// controller: helpController.helpQuestionController,
|
||||
// placeholder: 'Enter your Question here'.tr,
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius: BorderRadius.circular(12),
|
||||
// border: Border.all(
|
||||
// color: CupertinoColors.systemGrey,
|
||||
// ),
|
||||
// ),
|
||||
// padding: const EdgeInsets.all(16),
|
||||
// clearButtonMode: OverlayVisibilityMode.editing,
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
// helpController.isLoading
|
||||
// ? const CupertinoActivityIndicator()
|
||||
// : CupertinoButton.filled(
|
||||
// onPressed: () {
|
||||
// if (helpController.formKey.currentState!
|
||||
// .validate()) {
|
||||
// helpController.addHelpQuestion();
|
||||
|
||||
// // Clear the feedback form
|
||||
// helpController.formKey.currentState!
|
||||
// .reset();
|
||||
// }
|
||||
// },
|
||||
// child: Text('Submit Question'.tr),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
// Expanded(
|
||||
// child: GetBuilder<HelpController>(
|
||||
// builder: (helpController) => Padding(
|
||||
// padding: const EdgeInsets.all(10),
|
||||
// child: Container(
|
||||
// decoration: BoxDecoration(
|
||||
// border: Border.all(
|
||||
// color: CupertinoColors.systemGrey2,
|
||||
// ),
|
||||
// borderRadius: BorderRadius.circular(12.0),
|
||||
// ),
|
||||
// child: ListView.builder(
|
||||
// itemCount: helpController.helpQuestionDate['message'] !=
|
||||
// null
|
||||
// ? helpController.helpQuestionDate['message'].length
|
||||
// : 0,
|
||||
// itemBuilder: (BuildContext context, int index) {
|
||||
// var list =
|
||||
// helpController.helpQuestionDate['message'][index];
|
||||
// return Padding(
|
||||
// padding: const EdgeInsets.all(3),
|
||||
// child: Container(
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// vertical: 12, horizontal: 16),
|
||||
// decoration: BoxDecoration(
|
||||
// border: Border.all(
|
||||
// color: CupertinoColors.activeGreen,
|
||||
// width: 2,
|
||||
// ),
|
||||
// borderRadius: BorderRadius.circular(12),
|
||||
// ),
|
||||
// child: GestureDetector(
|
||||
// onTap: () {
|
||||
// helpController.getIndex(
|
||||
// list['id'], list['helpQuestion']);
|
||||
// helpController
|
||||
// .getHelpRepley(list['id'].toString());
|
||||
// Get.to(() => const HelpDetailsReplayPage());
|
||||
// },
|
||||
// child: Row(
|
||||
// mainAxisAlignment:
|
||||
// MainAxisAlignment.spaceBetween,
|
||||
// children: [
|
||||
// Expanded(
|
||||
// child: Text(
|
||||
// list['helpQuestion'],
|
||||
// style: CupertinoTheme.of(context)
|
||||
// .textTheme
|
||||
// .textStyle,
|
||||
// overflow: TextOverflow.ellipsis,
|
||||
// ),
|
||||
// ),
|
||||
// Text(
|
||||
// list['datecreated'],
|
||||
// style: CupertinoTheme.of(context)
|
||||
// .textTheme
|
||||
// .tabLabelTextStyle,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
85
lib/views/home/Captin/home_captain/help_details_replay_page.dart
Executable file
85
lib/views/home/Captin/home_captain/help_details_replay_page.dart
Executable file
@@ -0,0 +1,85 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
import 'package:sefer_driver/views/widgets/mycircular.dart';
|
||||
|
||||
import '../../../../controller/functions/encrypt_decrypt.dart';
|
||||
import '../../../../controller/home/captin/help/help_controller.dart';
|
||||
import '../../../widgets/my_scafold.dart';
|
||||
|
||||
class HelpDetailsReplayPage extends StatelessWidget {
|
||||
const HelpDetailsReplayPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.find<HelpController>();
|
||||
return GetBuilder<HelpController>(
|
||||
builder: (helpController) => MyScafolld(
|
||||
title: 'Help Details'.tr,
|
||||
body: [
|
||||
helpController.isLoading
|
||||
? const MyCircularProgressIndicator()
|
||||
: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Card(
|
||||
elevation: 3,
|
||||
child: Container(
|
||||
width: Get.width * .66,
|
||||
color: Colors.transparent,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
helpController.qustion,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Card(
|
||||
elevation: 3,
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
width: Get.width * .66,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: helpController.status ==
|
||||
'not yet' ||
|
||||
EncryptionHelper.instance
|
||||
.decryptData(helpController
|
||||
.helpQuestionRepleyDate[
|
||||
'message']['replay'])
|
||||
.toString() ==
|
||||
'not yet'
|
||||
? Text(
|
||||
'No Response yet.'.tr,
|
||||
style: AppStyle.title,
|
||||
)
|
||||
: Text(
|
||||
EncryptionHelper.instance
|
||||
.decryptData(helpController
|
||||
.helpQuestionRepleyDate[
|
||||
'message']['replay'])
|
||||
.toString(),
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
],
|
||||
)
|
||||
],
|
||||
isleading: true,
|
||||
));
|
||||
}
|
||||
}
|
||||
750
lib/views/home/Captin/home_captain/home_captin.dart
Executable file
750
lib/views/home/Captin/home_captain/home_captin.dart
Executable file
@@ -0,0 +1,750 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:sefer_driver/constant/box_name.dart';
|
||||
import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart';
|
||||
import 'package:sefer_driver/views/notification/available_rides_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
||||
import 'package:sefer_driver/views/home/Captin/home_captain/drawer_captain.dart';
|
||||
import 'package:sefer_driver/views/widgets/mycircular.dart';
|
||||
import 'package:bubble_head/bubble.dart';
|
||||
|
||||
import '../../../../constant/colors.dart';
|
||||
import '../../../../constant/info.dart';
|
||||
import '../../../../constant/style.dart';
|
||||
import '../../../../controller/functions/location_controller.dart';
|
||||
import '../../../../controller/functions/overlay_permisssion.dart';
|
||||
import '../../../../controller/functions/package_info.dart';
|
||||
import '../../../../controller/home/captin/home_captain_controller.dart';
|
||||
import '../../../../print.dart';
|
||||
import '../../../widgets/circle_container.dart';
|
||||
import '../driver_map_page.dart';
|
||||
import 'widget/connect.dart';
|
||||
import 'widget/left_menu_map_captain.dart';
|
||||
import '../../../../main.dart';
|
||||
|
||||
// =================================================================
|
||||
// STEP 1: Modify your LocationController
|
||||
// =================================================================
|
||||
/*
|
||||
In your `location_controller.dart` file, change `myLocation` and `heading`
|
||||
to be observable by adding `.obs`. This will allow other parts of your app,
|
||||
like the map, to automatically react to changes.
|
||||
|
||||
// BEFORE:
|
||||
// LatLng myLocation = LatLng(....);
|
||||
// double heading = 0.0;
|
||||
|
||||
// AFTER:
|
||||
final myLocation = const LatLng(30.0444, 31.2357).obs; // Default to Cairo or a sensible default
|
||||
final heading = 0.0.obs;
|
||||
|
||||
// When you update these values elsewhere in your controller,
|
||||
// make sure to update their `.value` property.
|
||||
// e.g., myLocation.value = newLatLng;
|
||||
// e.g., heading.value = newHeading;
|
||||
*/
|
||||
|
||||
// =================================================================
|
||||
// STEP 2: Modify your HomeCaptainController
|
||||
// =================================================================
|
||||
/*
|
||||
In your `home_captain_controller.dart` file, you need to add logic to
|
||||
listen for changes from the LocationController and animate the camera.
|
||||
|
||||
class HomeCaptainController extends GetxController {
|
||||
// ... your existing variables (mapController, carIcon, etc.)
|
||||
|
||||
// Make sure you have a reference to the GoogleMapController
|
||||
GoogleMapController? mapHomeCaptainController;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
_setupLocationListener();
|
||||
}
|
||||
|
||||
void onMapCreated(GoogleMapController controller) {
|
||||
mapHomeCaptainController = controller;
|
||||
// Any other map setup logic
|
||||
}
|
||||
|
||||
// THIS IS THE NEW LOGIC TO ADD
|
||||
void _setupLocationListener() {
|
||||
final locationController = Get.find<LocationController>();
|
||||
|
||||
// The 'ever' worker from GetX listens for changes to an observable variable.
|
||||
// Whenever `heading` or `myLocation` changes, it will call our method.
|
||||
ever(locationController.heading, (_) => _updateCameraPosition());
|
||||
ever(locationController.myLocation, (_) => _updateCameraPosition());
|
||||
}
|
||||
|
||||
void _updateCameraPosition() {
|
||||
final locationController = Get.find<LocationController>();
|
||||
if (mapHomeCaptainController != null) {
|
||||
final newPosition = CameraPosition(
|
||||
target: locationController.myLocation.value,
|
||||
zoom: 17.5, // A bit closer for a navigation feel
|
||||
tilt: 50.0, // A nice 3D perspective
|
||||
bearing: locationController.heading.value, // This rotates the map
|
||||
);
|
||||
|
||||
// Animate the camera smoothly to the new position and rotation
|
||||
mapHomeCaptainController!.animateCamera(
|
||||
CameraUpdate.newCameraPosition(newPosition),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ... rest of your controller code
|
||||
}
|
||||
*/
|
||||
|
||||
// =================================================================
|
||||
// STEP 3: Update the HomeCaptain Widget
|
||||
// =================================================================
|
||||
class HomeCaptain extends StatelessWidget {
|
||||
HomeCaptain({super.key});
|
||||
final LocationController locationController = Get.put(LocationController());
|
||||
final HomeCaptainController homeCaptainController =
|
||||
Get.put(HomeCaptainController());
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(HomeCaptainController());
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
closeOverlayIfFound();
|
||||
checkForUpdate(context);
|
||||
getPermissionOverlay();
|
||||
showDriverGiftClaim(context);
|
||||
});
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
elevation: 2,
|
||||
flexibleSpace: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [Colors.white, Colors.grey.shade50],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.1),
|
||||
spreadRadius: 1,
|
||||
blurRadius: 4,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
title: Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/images/logo.gif',
|
||||
height: 32,
|
||||
width: 35,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
AppInformation.appName.split(' ')[0].toString().tr,
|
||||
style: AppStyle.title.copyWith(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColor.blueColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: MyCircleContainer(
|
||||
child: Text(
|
||||
homeCaptainController.countRefuse.toString(),
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.1),
|
||||
spreadRadius: 1,
|
||||
blurRadius: 4,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
_MapControlButton(
|
||||
icon: Icons.satellite_alt,
|
||||
tooltip: 'Change Map Type'.tr,
|
||||
onPressed: homeCaptainController.changeMapType,
|
||||
),
|
||||
_MapControlButton(
|
||||
icon: Icons.streetview_sharp,
|
||||
tooltip: 'Toggle Traffic'.tr,
|
||||
onPressed: homeCaptainController.changeMapTraffic,
|
||||
),
|
||||
_MapControlButton(
|
||||
icon: Icons.my_location, // Changed for clarity
|
||||
tooltip: 'Center on Me'.tr,
|
||||
onPressed: () {
|
||||
// This button now just re-centers without changing rotation
|
||||
if (homeCaptainController.mapHomeCaptainController !=
|
||||
null) {
|
||||
homeCaptainController.mapHomeCaptainController!
|
||||
.animateCamera(CameraUpdate.newLatLngZoom(
|
||||
Get.find<LocationController>().myLocation,
|
||||
17.5,
|
||||
));
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
),
|
||||
drawer: CupertinoDrawerCaptain(),
|
||||
body: Stack(
|
||||
children: [
|
||||
// FIX: Replaced nested GetBuilder/Obx with a single GetX widget.
|
||||
// GetX handles both observable (.obs) variables and standard controller updates.
|
||||
GetBuilder<HomeCaptainController>(builder: (controller) {
|
||||
return controller.isLoading
|
||||
? const MyCircularProgressIndicator()
|
||||
: GoogleMap(
|
||||
onMapCreated: controller.onMapCreated,
|
||||
minMaxZoomPreference: const MinMaxZoomPreference(6, 18),
|
||||
initialCameraPosition: CameraPosition(
|
||||
// Use .value to get the latest location from the reactive variable
|
||||
target: locationController.myLocation,
|
||||
zoom: 15,
|
||||
),
|
||||
onCameraMove: (position) {
|
||||
CameraPosition(
|
||||
target: locationController.myLocation,
|
||||
zoom: 17.5, // A bit closer for a navigation feel
|
||||
tilt: 50.0, // A nice 3D perspective
|
||||
bearing:
|
||||
locationController.heading, // This rotates the map
|
||||
);
|
||||
},
|
||||
markers: {
|
||||
Marker(
|
||||
markerId: MarkerId('MyLocation'.tr),
|
||||
// Use .value for position and rotation from the reactive variable
|
||||
position: locationController.myLocation,
|
||||
rotation: locationController.heading,
|
||||
// IMPORTANT: These two properties make the marker look
|
||||
// correct when the map is tilted and rotating.
|
||||
flat: true,
|
||||
anchor: const Offset(0.5, 0.5),
|
||||
icon: controller.carIcon,
|
||||
)
|
||||
},
|
||||
mapType: controller.mapType
|
||||
? MapType.satellite
|
||||
: MapType.terrain,
|
||||
myLocationButtonEnabled: false, // Disable default button
|
||||
myLocationEnabled: false, // We use our custom marker
|
||||
trafficEnabled: controller.mapTrafficON,
|
||||
buildingsEnabled: true,
|
||||
mapToolbarEnabled: true,
|
||||
zoomControlsEnabled: false, // Cleaner UI for navigation
|
||||
);
|
||||
}),
|
||||
|
||||
// The rest of your UI remains the same...
|
||||
Positioned(
|
||||
bottom: 10,
|
||||
right: Get.width * .1,
|
||||
left: Get.width * .1,
|
||||
child: const ConnectWidget()),
|
||||
Positioned(
|
||||
top: 5,
|
||||
right: Get.width * .05,
|
||||
left: Get.width * .05,
|
||||
child: GetBuilder<HomeCaptainController>(
|
||||
builder: (homeCaptainController) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: const LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [Colors.white, Colors.white70],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.2),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
width: Get.width * .8,
|
||||
height: 120,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.greenColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Entypo.wallet,
|
||||
color: AppColor.greenColor,
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'${"Today".tr}: ${(homeCaptainController.totalMoneyToday)}',
|
||||
style: AppStyle.title.copyWith(
|
||||
color: AppColor.greenColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.yellowColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Entypo.wallet,
|
||||
color: AppColor.yellowColor,
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'${AppInformation.appName}: ${(homeCaptainController.totalMoneyInSEFER)}',
|
||||
style: AppStyle.title.copyWith(
|
||||
color: AppColor.yellowColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'${'Total Points is'.tr}: ${(homeCaptainController.totalPoints)}',
|
||||
style: AppStyle.title.copyWith(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: int.parse((homeCaptainController
|
||||
.countRideToday)) <
|
||||
5
|
||||
? AppColor.accentColor
|
||||
: int.parse((homeCaptainController
|
||||
.countRideToday)) >
|
||||
5 &&
|
||||
int.parse((homeCaptainController
|
||||
.countRideToday)) <
|
||||
10
|
||||
? AppColor.yellowColor
|
||||
: AppColor.greenColor,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.directions_car_rounded,
|
||||
color: Colors.white,
|
||||
size: 18,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'${"Ride Today : ".tr}: ${(homeCaptainController.countRideToday)}',
|
||||
style: AppStyle.title.copyWith(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 65,
|
||||
right: Get.width * .1,
|
||||
left: Get.width * .1,
|
||||
child: GetBuilder<HomeCaptainController>(
|
||||
builder: (homeCaptainController) => Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.2),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.timer_outlined,
|
||||
color: AppColor.greenColor),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Active Duration:'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
(homeCaptainController.stringActiveDuration),
|
||||
style: AppStyle.title.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.greenColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.access_time,
|
||||
color: AppColor.accentColor),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Total Connection Duration:'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
(homeCaptainController.totalDurationToday),
|
||||
style: AppStyle.title.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.accentColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: Get.height * .2,
|
||||
right: 6,
|
||||
child: Column(
|
||||
children: [
|
||||
Platform.isAndroid
|
||||
? AnimatedContainer(
|
||||
duration: const Duration(microseconds: 200),
|
||||
width: homeCaptainController.widthMapTypeAndTraffic,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: AppColor.blueColor),
|
||||
color: AppColor.secondaryColor,
|
||||
borderRadius: BorderRadius.circular(15)),
|
||||
child: IconButton(
|
||||
onPressed: () async {
|
||||
Bubble().startBubbleHead(sendAppToBackground: true);
|
||||
},
|
||||
icon: Image.asset(
|
||||
'assets/images/logo1.png',
|
||||
fit: BoxFit.cover,
|
||||
width: 35,
|
||||
height: 35,
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
AnimatedContainer(
|
||||
duration: const Duration(microseconds: 200),
|
||||
width: homeCaptainController.widthMapTypeAndTraffic,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: AppColor.blueColor),
|
||||
color: AppColor.secondaryColor,
|
||||
borderRadius: BorderRadius.circular(15)),
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
Get.to(() => const AvailableRidesPage());
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.train_sharp,
|
||||
size: 29,
|
||||
color: AppColor.blueColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
GetBuilder<HomeCaptainController>(
|
||||
builder: (homeCaptainController) {
|
||||
return box.read(BoxName.rideStatus) == 'Applied' ||
|
||||
box.read(BoxName.rideStatus) == 'Begin'
|
||||
? Positioned(
|
||||
bottom: Get.height * .2,
|
||||
right: 6,
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(microseconds: 200),
|
||||
width: homeCaptainController.widthMapTypeAndTraffic,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: AppColor.blueColor),
|
||||
color: AppColor.secondaryColor,
|
||||
borderRadius: BorderRadius.circular(15)),
|
||||
child: GestureDetector(
|
||||
onLongPress: () {
|
||||
box.write(BoxName.rideStatus, 'delete');
|
||||
homeCaptainController.update();
|
||||
},
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
box.read(BoxName.rideStatus) == 'Applied'
|
||||
? {
|
||||
Get.to(
|
||||
() => PassengerLocationMapPage(),
|
||||
arguments: box
|
||||
.read(BoxName.rideArguments)),
|
||||
Get.put(MapDriverController())
|
||||
.changeRideToBeginToPassenger()
|
||||
}
|
||||
: {
|
||||
Get.to(
|
||||
() => PassengerLocationMapPage(),
|
||||
arguments: box
|
||||
.read(BoxName.rideArguments)),
|
||||
Get.put(MapDriverController())
|
||||
.startRideFromStartApp()
|
||||
};
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.directions_rounded,
|
||||
size: 29,
|
||||
color: AppColor.blueColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox();
|
||||
})
|
||||
],
|
||||
),
|
||||
),
|
||||
leftMainMenuCaptainIcons(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// These helper widgets and functions remain unchanged
|
||||
showFirstTimeOfferNotification(BuildContext context) async {
|
||||
bool isFirstTime = _checkIfFirstTime();
|
||||
|
||||
if (isFirstTime) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.rectangle,
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Colors.black26,
|
||||
blurRadius: 10.0,
|
||||
offset: Offset(0.0, 10.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Welcome Offer!'.tr,
|
||||
style: const TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
Text(
|
||||
'As a new driver, you\'re eligible for a special offer!'.tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Stack(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.green,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
child: Text(
|
||||
'300 LE'.tr,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 25,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: -10,
|
||||
top: -10,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(5),
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.red,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.attach_money,
|
||||
color: Colors.white,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
'for your first registration!'.tr,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.green,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20, vertical: 10),
|
||||
child: Text(
|
||||
"Get it Now!".tr,
|
||||
style:
|
||||
const TextStyle(fontSize: 18, color: Colors.white),
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
_markAsNotFirstTime();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool _checkIfFirstTime() {
|
||||
if (box.read(BoxName.isFirstTime).toString() == '') {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void _markAsNotFirstTime() {
|
||||
box.write(BoxName.isFirstTime, 'false');
|
||||
}
|
||||
|
||||
class _MapControlButton extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final VoidCallback onPressed;
|
||||
final String tooltip;
|
||||
|
||||
const _MapControlButton({
|
||||
required this.icon,
|
||||
required this.onPressed,
|
||||
required this.tooltip,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Tooltip(
|
||||
message: tooltip,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
onTap: onPressed,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Icon(
|
||||
icon,
|
||||
size: 24,
|
||||
color: AppColor.blueColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
84
lib/views/home/Captin/home_captain/widget/call_page.dart
Executable file
84
lib/views/home/Captin/home_captain/widget/call_page.dart
Executable file
@@ -0,0 +1,84 @@
|
||||
// import 'package:SEFER/constant/colors.dart';
|
||||
// import 'package:SEFER/constant/style.dart';
|
||||
// import 'package:SEFER/controller/firebase/firbase_messge.dart';
|
||||
// import 'package:SEFER/controller/home/captin/map_driver_controller.dart';
|
||||
// import 'package:SEFER/views/widgets/my_scafold.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:get/get.dart';
|
||||
// import 'package:SEFER/controller/home/captin/home_captain_controller.dart';
|
||||
|
||||
// import '../../../../../controller/functions/call_controller.dart';
|
||||
|
||||
// class CallPage extends StatelessWidget {
|
||||
// const CallPage({super.key});
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return MyScafolld(
|
||||
// title: 'Call Page'.tr, isleading: true, body: [callPage()]);
|
||||
// }
|
||||
// }
|
||||
|
||||
// GetBuilder<HomeCaptainController> callPage() {
|
||||
// CallController callController = Get.put(CallController());
|
||||
// Get.put(MapDriverController());
|
||||
// // callController.join();
|
||||
// return GetBuilder<HomeCaptainController>(
|
||||
// builder: (controller) => Positioned(
|
||||
// top: Get.height * .2,
|
||||
// child: Container(
|
||||
// height: 100, width: Get.width,
|
||||
// decoration: AppStyle.boxDecoration,
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
// children: [
|
||||
// GestureDetector(
|
||||
// onTap: () async {
|
||||
// callController.join();
|
||||
// },
|
||||
// child: Container(
|
||||
// width: 50,
|
||||
// height: 50,
|
||||
// decoration: const BoxDecoration(
|
||||
// shape: BoxShape.circle, color: AppColor.greenColor),
|
||||
// child: const Icon(
|
||||
// Icons.phone,
|
||||
// size: 35,
|
||||
// color: AppColor.secondaryColor,
|
||||
// )),
|
||||
// ),
|
||||
// Column(
|
||||
// children: [
|
||||
// Text(callController.status),
|
||||
// Text(Get.find<MapDriverController>().passengerName.toString()),
|
||||
// ],
|
||||
// ),
|
||||
// GestureDetector(
|
||||
// onTap: () async {
|
||||
// FirebaseMessagesController().sendNotificationToPassengerToken(
|
||||
// 'Call End'.tr,
|
||||
// 'Call End',
|
||||
// Get.find<MapDriverController>().tokenPassenger,
|
||||
// [],
|
||||
// 'iphone_ringtone.wav');
|
||||
// callController.leave();
|
||||
// Get.back();
|
||||
// },
|
||||
// child: Container(
|
||||
// width: 50,
|
||||
// height: 50,
|
||||
// decoration: const BoxDecoration(
|
||||
// shape: BoxShape.circle, color: AppColor.redColor),
|
||||
// child: const Icon(
|
||||
// Icons.phone_disabled_sharp,
|
||||
// size: 35,
|
||||
// color: AppColor.secondaryColor,
|
||||
// )),
|
||||
// )
|
||||
// ],
|
||||
// ),
|
||||
// // ignore: prefer_const_constructors
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
141
lib/views/home/Captin/home_captain/widget/connect.dart
Executable file
141
lib/views/home/Captin/home_captain/widget/connect.dart
Executable file
@@ -0,0 +1,141 @@
|
||||
import 'package:sefer_driver/controller/functions/tts.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/controller/home/payment/captain_wallet_controller.dart';
|
||||
|
||||
import '../../../../../constant/style.dart';
|
||||
import '../../../../../controller/functions/encrypt_decrypt.dart';
|
||||
import '../../../../widgets/elevated_btn.dart';
|
||||
import '../../../../../controller/home/captin/home_captain_controller.dart';
|
||||
|
||||
class ConnectWidget extends StatelessWidget {
|
||||
const ConnectWidget({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// final OrderRequestController orderRequestController =
|
||||
// Get.put(OrderRequestController());
|
||||
CaptainWalletController captainWalletController =
|
||||
Get.put(CaptainWalletController());
|
||||
|
||||
captainWalletController.getCaptainWalletFromBuyPoints();
|
||||
return Center(
|
||||
child: GetBuilder<HomeCaptainController>(
|
||||
builder: (homeCaptainController) => double.parse(
|
||||
(captainWalletController.totalPoints)) <
|
||||
-300
|
||||
? CupertinoButton(
|
||||
onPressed: () {
|
||||
Get.defaultDialog(
|
||||
// backgroundColor: CupertinoColors.destructiveRed,
|
||||
barrierDismissible: false,
|
||||
title: double.parse(
|
||||
(captainWalletController.totalPoints)) <
|
||||
-300
|
||||
? 'You dont have Points'.tr
|
||||
: 'You Are Stopped For this Day !'.tr,
|
||||
titleStyle: AppStyle.title,
|
||||
content: Column(
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
double.parse((captainWalletController
|
||||
.totalPoints)) <
|
||||
-300
|
||||
? await Get.find<TextToSpeechController>()
|
||||
.speakText(
|
||||
'You must be recharge your Account'
|
||||
.tr)
|
||||
: await Get.find<TextToSpeechController>()
|
||||
.speakText(
|
||||
'You Refused 3 Rides this Day that is the reason \nSee you Tomorrow!'
|
||||
.tr);
|
||||
},
|
||||
icon: const Icon(Icons.headphones),
|
||||
),
|
||||
Text(
|
||||
double.parse((captainWalletController
|
||||
.totalPoints)) <
|
||||
-300
|
||||
? 'You must be recharge your Account'.tr
|
||||
: 'You Refused 3 Rides this Day that is the reason \nSee you Tomorrow!'
|
||||
.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
confirm: double.parse(
|
||||
(captainWalletController.totalPoints)) <
|
||||
-300
|
||||
? MyElevatedButton(
|
||||
title: 'Recharge my Account'.tr,
|
||||
onPressed: () {
|
||||
homeCaptainController.goToWalletFromConnect();
|
||||
})
|
||||
: MyElevatedButton(
|
||||
title: 'Ok , See you Tomorrow'.tr,
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
Get.back();
|
||||
}));
|
||||
},
|
||||
color: CupertinoColors.destructiveRed,
|
||||
child: Text(
|
||||
'You are Stopped'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: homeCaptainController.isActive
|
||||
? [Colors.green.shade400, Colors.green.shade700]
|
||||
: [Colors.grey.shade400, Colors.grey.shade700],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: homeCaptainController.isActive
|
||||
? Colors.green.withOpacity(0.3)
|
||||
: Colors.grey.withOpacity(0.3),
|
||||
spreadRadius: 1,
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: CupertinoButton(
|
||||
onPressed: homeCaptainController.onButtonSelected,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24, vertical: 12),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
homeCaptainController.isActive
|
||||
? CupertinoIcons.check_mark_circled_solid
|
||||
: CupertinoIcons.circle,
|
||||
color: Colors.white,
|
||||
size: 24,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
homeCaptainController.isActive
|
||||
? 'Connected'.tr
|
||||
: 'Not Connected'.tr,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
308
lib/views/home/Captin/home_captain/widget/left_menu_map_captain.dart
Executable file
308
lib/views/home/Captin/home_captain/widget/left_menu_map_captain.dart
Executable file
@@ -0,0 +1,308 @@
|
||||
import 'package:sefer_driver/constant/box_name.dart';
|
||||
import 'package:sefer_driver/controller/firebase/local_notification.dart';
|
||||
import 'package:sefer_driver/main.dart';
|
||||
import 'package:sefer_driver/views/auth/captin/login_captin.dart';
|
||||
import 'package:sefer_driver/views/home/Captin/driver_map_page.dart';
|
||||
import 'package:sefer_driver/views/home/Captin/orderCaptin/vip_order_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart';
|
||||
import 'package:sefer_driver/views/home/my_wallet/points_captain.dart';
|
||||
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
||||
|
||||
import '../../../../../constant/colors.dart';
|
||||
import '../../../../../constant/links.dart';
|
||||
import '../../../../../controller/auth/google_sign.dart';
|
||||
import '../../../../../controller/firebase/firbase_messge.dart';
|
||||
import '../../../../../controller/functions/crud.dart';
|
||||
import '../../../../../controller/home/captin/order_request_controller.dart';
|
||||
import '../../../../Rate/ride_calculate_driver.dart';
|
||||
import '../../../../auth/captin/otp_page.dart';
|
||||
import '../../../my_wallet/ecash.dart';
|
||||
|
||||
GetBuilder<HomeCaptainController> leftMainMenuCaptainIcons() {
|
||||
final firebaseMessagesController =
|
||||
Get.isRegistered<FirebaseMessagesController>()
|
||||
? Get.find<FirebaseMessagesController>()
|
||||
: Get.put(FirebaseMessagesController());
|
||||
return GetBuilder<HomeCaptainController>(
|
||||
builder: (controller) => Positioned(
|
||||
bottom: Get.height * .2,
|
||||
left: 6,
|
||||
child: Column(
|
||||
children: [
|
||||
AnimatedContainer(
|
||||
duration: const Duration(microseconds: 200),
|
||||
width: controller.widthMapTypeAndTraffic,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.secondaryColor,
|
||||
border: Border.all(color: AppColor.blueColor),
|
||||
borderRadius: BorderRadius.circular(15)),
|
||||
child: Builder(builder: (context) {
|
||||
return IconButton(
|
||||
onPressed: () async {
|
||||
await checkForPendingOrderFromServer();
|
||||
box.read(BoxName.rideArgumentsFromBackground) != 'failure'
|
||||
? Get.to(() => PassengerLocationMapPage(),
|
||||
arguments:
|
||||
box.read(BoxName.rideArgumentsFromBackground))
|
||||
: MyDialog().getDialog(
|
||||
'Ride info'.tr,
|
||||
'you dont have accepted ride'.tr,
|
||||
() {
|
||||
Get.back();
|
||||
},
|
||||
);
|
||||
// 'box.read(BoxName.rideArgumentsFromBackground): ${box.read(BoxName.rideArgumentsFromBackground)}');
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.directions_car_rounded,
|
||||
size: 29,
|
||||
color:
|
||||
box.read(BoxName.rideArgumentsFromBackground) == 'failure'
|
||||
? AppColor.redColor
|
||||
: AppColor.greenColor,
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
AnimatedContainer(
|
||||
duration: const Duration(microseconds: 200),
|
||||
width: controller.widthMapTypeAndTraffic,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.secondaryColor,
|
||||
border: Border.all(color: AppColor.blueColor),
|
||||
borderRadius: BorderRadius.circular(15)),
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
// NotificationController1()
|
||||
// .showNotification('Sefer Driver'.tr, ''.tr, '', '');
|
||||
final now = DateTime.now();
|
||||
DateTime? lastRequestTime =
|
||||
box.read(BoxName.lastTimeStaticThrottle);
|
||||
|
||||
if (lastRequestTime == null ||
|
||||
now.difference(lastRequestTime).inMinutes >= 2) {
|
||||
// Update the last request time to now
|
||||
lastRequestTime = now;
|
||||
box.write(BoxName.lastTimeStaticThrottle, lastRequestTime);
|
||||
// Navigate to the RideCalculateDriver page
|
||||
Get.to(() => RideCalculateDriver());
|
||||
} else {
|
||||
// Optionally show a message or handle the throttling case
|
||||
final minutesLeft =
|
||||
2 - now.difference(lastRequestTime).inMinutes;
|
||||
// Get.snackbar(
|
||||
// '${'Please wait'.tr} $minutesLeft ${"minutes before trying again.".tr}',
|
||||
// '');
|
||||
NotificationController1().showNotification(
|
||||
'Intaleq Driver'.tr,
|
||||
'${'Please wait'.tr} $minutesLeft ${"minutes before trying again.".tr}',
|
||||
'ding',
|
||||
'');
|
||||
}
|
||||
},
|
||||
icon: const Icon(
|
||||
FontAwesome5.chart_bar,
|
||||
size: 29,
|
||||
color: AppColor.blueColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
// Platform.isAndroid
|
||||
// ?
|
||||
int.parse(box.read(BoxName.carYear).toString()) > 2023
|
||||
? AnimatedContainer(
|
||||
duration: const Duration(microseconds: 200),
|
||||
width: controller.widthMapTypeAndTraffic,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.secondaryColor,
|
||||
border: Border.all(color: AppColor.blueColor),
|
||||
borderRadius: BorderRadius.circular(15)),
|
||||
child: Builder(builder: (context) {
|
||||
return IconButton(
|
||||
onPressed: () async {
|
||||
// mySnakeBarError('ad');
|
||||
Get.to(() => const VipOrderPage());
|
||||
},
|
||||
icon: const Icon(
|
||||
Octicons.watch,
|
||||
size: 29,
|
||||
color: AppColor.blueColor,
|
||||
),
|
||||
);
|
||||
}),
|
||||
)
|
||||
: const SizedBox(),
|
||||
// const SizedBox(
|
||||
// height: 5,
|
||||
// ),
|
||||
|
||||
// AnimatedContainer(
|
||||
// duration: const Duration(microseconds: 200),
|
||||
// width: controller.widthMapTypeAndTraffic,
|
||||
// decoration: BoxDecoration(
|
||||
// color: AppColor.secondaryColor,
|
||||
// border: Border.all(color: AppColor.blueColor),
|
||||
// borderRadius: BorderRadius.circular(15)),
|
||||
// child: Builder(builder: (context) {
|
||||
// return IconButton(
|
||||
// onPressed: () async {
|
||||
// Get.to(PhoneNumberScreen());
|
||||
// // payWithEcashDriver(context, '2000');
|
||||
// // payWithMTNWallet(context, '1', 'SYP');
|
||||
// // firebaseMessagesController.sendNotificationToDriverMAP(
|
||||
// // 'title',
|
||||
// // DateTime.now().toString(),
|
||||
// // 'ffX7xVXpdE_Xq8JBH3lgS4:APA91bGBHp53E-ZuXdlLBpRZohzqR9sazqcn3pwpEDG7JxkVi9MBtFDlCipzLpPCvD6LHEtds88ugGyCty7pEJVyx6tQYvzHVDCh7l3_7axpyriTBs5iv9E',
|
||||
// // [],
|
||||
// // '');
|
||||
// // box.write(BoxName.statusDriverLocation, 'off');
|
||||
// },
|
||||
// icon: const Icon(
|
||||
// FontAwesome5.grin_tears,
|
||||
// size: 29,
|
||||
// color: AppColor.blueColor,
|
||||
// ),
|
||||
// );
|
||||
// }),
|
||||
// ),f
|
||||
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> checkForPendingOrderFromServer() async {
|
||||
bool _isProcessingOrder = false;
|
||||
if (_isProcessingOrder) return;
|
||||
|
||||
final driverId = box.read(BoxName.driverID)?.toString();
|
||||
if (driverId == null) return; // Can't check without a driver ID
|
||||
|
||||
_isProcessingOrder = true; // Lock
|
||||
|
||||
try {
|
||||
// You need to create this CRUD method
|
||||
var response = await CRUD().post(
|
||||
link: AppLink.getArgumentAfterAppliedFromBackground,
|
||||
payload: {'driver_id': driverId},
|
||||
);
|
||||
|
||||
// Assuming the server returns order data if found, or 'failure'/'none' if not
|
||||
if (response != 'failure') {
|
||||
final Map<String, dynamic> orderInfoFromServer = response['message'];
|
||||
final Map<String, dynamic> rideArguments =
|
||||
_transformServerDataToAppArguments(orderInfoFromServer);
|
||||
// 2. Build the new arguments map, matching your Flutter structure
|
||||
|
||||
/////////////
|
||||
final customerToken = (response)['message']['token_passenger'];
|
||||
final orderId = (response)['message']['ride_id'].toString();
|
||||
box.write(BoxName.rideArgumentsFromBackground, rideArguments);
|
||||
box.write(BoxName.statusDriverLocation, 'on');
|
||||
box.write(BoxName.rideStatus, 'Apply');
|
||||
Get.put(OrderRequestController()).changeApplied();
|
||||
// MyDialog().getDialog(orderId.toString(), customerToken, () {});
|
||||
|
||||
// Now proceed with the UI flow
|
||||
_sendAcceptanceNotification(customerToken, orderId.toString());
|
||||
// await _bringAppToForegroundAndNavigate(orderId);
|
||||
} else {
|
||||
box.write(BoxName.rideArgumentsFromBackground, 'failure');
|
||||
}
|
||||
} catch (e) {
|
||||
} finally {
|
||||
_isProcessingOrder = false; // Release lock
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> _transformServerDataToAppArguments(
|
||||
Map<String, dynamic> serverData) {
|
||||
// Helper function to safely get and convert values to String
|
||||
String _getString(String key, [String defaultValue = 'unknown']) {
|
||||
// serverData[key] might be an int, double, or string. .toString() handles all.
|
||||
// If it's null, use the default value.
|
||||
return serverData[key]?.toString() ?? defaultValue;
|
||||
}
|
||||
|
||||
return {
|
||||
'passengerLocation': _getString('passenger_location'),
|
||||
'passengerDestination': _getString('passenger_destination'),
|
||||
'Duration': _getString('duration'),
|
||||
'totalCost': _getString('total_cost'),
|
||||
'Distance': _getString('distance'),
|
||||
'name': _getString('name'),
|
||||
'phone': _getString('phone'),
|
||||
'email': _getString('email'),
|
||||
'tokenPassenger': _getString('token_passenger'),
|
||||
'direction': _getString('direction_url'),
|
||||
'DurationToPassenger': _getString('duration_to_passenger'),
|
||||
'rideId': _getString('ride_id'),
|
||||
'passengerId': _getString('passenger_id'),
|
||||
'driverId': _getString('driver_id'),
|
||||
'durationOfRideValue': _getString('duration_of_ride'),
|
||||
'paymentAmount': _getString('payment_amount'),
|
||||
'paymentMethod': _getString('payment_method'),
|
||||
'passengerWalletBurc': _getString('passenger_wallet_burc'),
|
||||
'timeOfOrder': _getString('time_of_order'),
|
||||
'totalPassenger': _getString('total_passenger'),
|
||||
'carType': _getString('car_type'),
|
||||
'kazan': _getString('kazan'),
|
||||
'startNameLocation': _getString('start_name_location'),
|
||||
'endNameLocation': _getString('end_name_location'),
|
||||
|
||||
// --- Special Handling ---
|
||||
|
||||
// Steps (handle null values by providing an empty string)
|
||||
'step0': _getString('step0'),
|
||||
'step1': _getString('step1'),
|
||||
'step2': _getString('step2'),
|
||||
'step3': _getString('step3'),
|
||||
'step4': _getString('step4'),
|
||||
|
||||
// Boolean conversion (1/0 from server to 'true'/'false' string for the app)
|
||||
'WalletChecked': (serverData['wallet_checked'] == 1).toString(),
|
||||
|
||||
// Logic-based conversion for isHaveSteps
|
||||
// Your app's `rideArguments` expects 'startEnd', so we provide that if has_steps is 1.
|
||||
// You might need to adjust this logic if 'haveSteps' is also a possibility.
|
||||
'isHaveSteps': (serverData['has_steps'] == 1)
|
||||
? 'startEnd'
|
||||
: 'noSteps', // Providing a default
|
||||
};
|
||||
}
|
||||
|
||||
void _sendAcceptanceNotification(String? customerToken, rideId) {
|
||||
try {
|
||||
if (customerToken == null) return;
|
||||
final FirebaseMessagesController _firebaseMessagesController =
|
||||
Get.put(FirebaseMessagesController());
|
||||
List<String> bodyToPassenger = [
|
||||
box.read(BoxName.driverID).toString(),
|
||||
box.read(BoxName.nameDriver).toString(),
|
||||
box.read(BoxName.tokenDriver).toString(),
|
||||
rideId.toString()
|
||||
];
|
||||
|
||||
// Safely check for customer token
|
||||
final String? token = customerToken;
|
||||
if (token != null && token.isNotEmpty) {
|
||||
_firebaseMessagesController.sendNotificationToDriverMAP('Accepted Ride',
|
||||
'your ride is applied'.tr, token, bodyToPassenger, 'start.wav');
|
||||
} else {}
|
||||
} catch (e) {}
|
||||
}
|
||||
67
lib/views/home/Captin/home_captain/widget/zones_controller.dart
Executable file
67
lib/views/home/Captin/home_captain/widget/zones_controller.dart
Executable file
@@ -0,0 +1,67 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:get/get.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
|
||||
class ZonesController extends GetxController {
|
||||
Map<String, List<LatLng>> generateZoneMap(
|
||||
LatLng southwest, LatLng southEast, LatLng northeast) {
|
||||
const double desiredZoneArea = 4; // in square kilometers
|
||||
|
||||
final double width = (southEast.longitude - southwest.longitude) * 100;
|
||||
final double height = (northeast.latitude - southEast.latitude) * 100;
|
||||
final double totalArea = width * height;
|
||||
|
||||
// final int numZones = (totalArea / desiredZoneArea).ceil();
|
||||
|
||||
final double zoneWidth = width / sqrt(desiredZoneArea);
|
||||
final double zoneHeight = height / sqrt(desiredZoneArea);
|
||||
final numRows =
|
||||
((northeast.latitude - southwest.latitude) / zoneHeight).ceil();
|
||||
final numCols =
|
||||
((southEast.longitude - southwest.longitude) / zoneWidth).ceil();
|
||||
List<String> zoneNames = [];
|
||||
List<LatLng> zoneCoordinates = [];
|
||||
|
||||
for (int row = 0; row < numRows; row++) {
|
||||
for (int col = 0; col < numCols; col++) {
|
||||
final double zoneSouthwestLat =
|
||||
southwest.latitude + (row * zoneHeight / 100);
|
||||
final double zoneSouthwestLng =
|
||||
southwest.longitude + (col * zoneWidth / 100);
|
||||
final double zoneNortheastLat = zoneSouthwestLat + zoneHeight / 100;
|
||||
final double zoneNortheastLng = zoneSouthwestLng + zoneWidth / 100;
|
||||
|
||||
LatLng zoneSouthwest = LatLng(zoneSouthwestLat, zoneSouthwestLng);
|
||||
LatLng zoneNortheast = LatLng(zoneNortheastLat, zoneNortheastLng);
|
||||
|
||||
String zoneName =
|
||||
'Zone${row + col}'; // Assign a unique name to each zone
|
||||
|
||||
zoneNames.add(zoneName);
|
||||
zoneCoordinates.add(zoneSouthwest);
|
||||
zoneCoordinates.add(zoneNortheast);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, List<LatLng>> zoneMap = {};
|
||||
for (int i = 0; i < zoneNames.length; i++) {
|
||||
zoneMap[zoneNames[i]] = [
|
||||
zoneCoordinates[i], // Southwest LatLng
|
||||
zoneCoordinates[i + 1], // Northeast LatLng
|
||||
];
|
||||
}
|
||||
|
||||
return zoneMap;
|
||||
}
|
||||
|
||||
void getJsonOfZones() {
|
||||
LatLng southwest = const LatLng(32.111107, 36.062222);
|
||||
LatLng southEast = const LatLng(32.108333, 36.101667);
|
||||
LatLng northeast = const LatLng(32.143889, 36.058889);
|
||||
Map<String, List<LatLng>> zoneMap =
|
||||
generateZoneMap(southwest, southEast, northeast);
|
||||
String jsonMap = json.encode(zoneMap);
|
||||
}
|
||||
}
|
||||
271
lib/views/home/Captin/maintain_center_page.dart
Executable file
271
lib/views/home/Captin/maintain_center_page.dart
Executable file
@@ -0,0 +1,271 @@
|
||||
import 'package:sefer_driver/constant/colors.dart';
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
import 'package:sefer_driver/controller/home/captin/help/maintain_center_controller.dart';
|
||||
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
|
||||
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
// class MaintainCenterPage extends StatelessWidget {
|
||||
// MaintainCenterPage({super.key});
|
||||
// MaintainCenterController maintainCenterController =
|
||||
// Get.put(MaintainCenterController());
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return MyScafolld(
|
||||
// title: "Maintenance Center".tr,
|
||||
// body: [
|
||||
// GetBuilder<MaintainCenterController>(
|
||||
// builder: (maintainCenterController) {
|
||||
// return Padding(
|
||||
// padding: const EdgeInsets.all(8.0),
|
||||
// child: Column(
|
||||
// children: [
|
||||
// Text(
|
||||
// "When you complete 600 trips, you will be eligible to receive offers for maintenance of your car."
|
||||
// .tr),
|
||||
// const SizedBox(
|
||||
// height: 10,
|
||||
// ),
|
||||
// Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
// children: [
|
||||
// MyElevatedButton(
|
||||
// title: "Show My Trip Count".tr,
|
||||
// onPressed: () async {
|
||||
// maintainCenterController.getTripCountByCaptain();
|
||||
// }),
|
||||
// _buildPriceAvatar(
|
||||
// maintainCenterController.tripCount['count'] == null
|
||||
// ? '0'
|
||||
// : maintainCenterController.tripCount['count']
|
||||
// .toString())
|
||||
// ],
|
||||
// ),
|
||||
// const SizedBox(
|
||||
// height: 10,
|
||||
// ),
|
||||
// Container(
|
||||
// decoration: AppStyle.boxDecoration,
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.all(14),
|
||||
// child: Text(
|
||||
// "We have maintenance offers for your car. You can use them after completing 600 trips to get a 20% discount on car repairs. Enjoy using our Tripz app and be part of our Tripz family."
|
||||
// .tr,
|
||||
// style: AppStyle.title,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(
|
||||
// height: 10,
|
||||
// ),
|
||||
// MyElevatedButton(
|
||||
// title: 'Show maintenance center near my location'.tr,
|
||||
// onPressed: () {
|
||||
// if (maintainCenterController.tripCount['count'] > 600) {
|
||||
// } else {
|
||||
// Get.snackbar("You should complete 600 trips".tr, '',
|
||||
// backgroundColor: AppColor.yellowColor);
|
||||
// }
|
||||
// })
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// })
|
||||
// ],
|
||||
// isleading: true);
|
||||
// }
|
||||
|
||||
// Widget _buildPriceAvatar(String count) {
|
||||
// return Container(
|
||||
// width: 80,
|
||||
// height: 80,
|
||||
// decoration: BoxDecoration(
|
||||
// shape: BoxShape.circle,
|
||||
// gradient: const RadialGradient(
|
||||
// colors: [Color(0xFF4CAF50), Color(0xFF2E7D32)],
|
||||
// center: Alignment.center,
|
||||
// radius: 0.8,
|
||||
// ),
|
||||
// boxShadow: [
|
||||
// BoxShadow(
|
||||
// color: Colors.black.withOpacity(0.2),
|
||||
// blurRadius: 8,
|
||||
// offset: const Offset(0, 4),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// child: Center(
|
||||
// child: Text(
|
||||
// count,
|
||||
// style: const TextStyle(
|
||||
// fontSize: 22,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: Colors.white,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
class MaintainCenterPage extends StatelessWidget {
|
||||
MaintainCenterPage({super.key});
|
||||
final MaintainCenterController maintainCenterController =
|
||||
Get.put(MaintainCenterController());
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MyScafolld(
|
||||
title: "Maintenance Center".tr,
|
||||
body: [
|
||||
GetBuilder<MaintainCenterController>(
|
||||
builder: (controller) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Introduction Text with better styling
|
||||
Text(
|
||||
"When you complete 600 trips, you will be eligible to receive offers for maintenance of your car."
|
||||
.tr,
|
||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(
|
||||
color: Colors.grey[700],
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Trip Count Section in a Card
|
||||
Card(
|
||||
elevation: 4,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: MyElevatedButton(
|
||||
title: "Show My Trip Count".tr,
|
||||
onPressed: () async {
|
||||
controller.getTripCountByCaptain();
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
_buildPriceAvatar(
|
||||
controller.tripCount['count'] == null
|
||||
? '0'
|
||||
: controller.tripCount['count'].toString(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Maintenance Offer Information in a Card
|
||||
Card(
|
||||
elevation: 4,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Maintenance Offer".tr,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
"We have maintenance offers for your car. You can use them after completing 600 trips to get a 20% discount on car repairs. Enjoy using our Tripz app and be part of our Tripz family."
|
||||
.tr,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(
|
||||
color: Colors.grey[800],
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Show Maintenance Center Button
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: MyElevatedButton(
|
||||
title: 'Show maintenance center near my location'.tr,
|
||||
onPressed: () {
|
||||
if (controller.tripCount['count'] != null &&
|
||||
controller.tripCount['count'] >= 600) {
|
||||
// Implement navigation or action to show maintenance centers
|
||||
// For now, let's print a message
|
||||
print("Navigating to maintenance centers...");
|
||||
} else {
|
||||
Get.snackbar(
|
||||
"Ineligible for Offer".tr,
|
||||
"You should complete 500 trips to unlock this feature."
|
||||
.tr,
|
||||
backgroundColor: AppColor.yellowColor,
|
||||
colorText: Colors.black,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
isleading: true,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPriceAvatar(String count) {
|
||||
return Container(
|
||||
width: 70, // Slightly reduced size
|
||||
height: 70,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: const LinearGradient(
|
||||
// Changed to LinearGradient
|
||||
colors: [Color(0xFF4CAF50), Color(0xFF2E7D32)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.15), // Reduced opacity
|
||||
blurRadius: 6, // Slightly reduced blur
|
||||
offset: const Offset(0, 3),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
count,
|
||||
style: const TextStyle(
|
||||
fontSize: 20, // Slightly reduced size
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
307
lib/views/home/Captin/mapDriverWidgets/driver_end_ride_bar.dart
Executable file
307
lib/views/home/Captin/mapDriverWidgets/driver_end_ride_bar.dart
Executable file
@@ -0,0 +1,307 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:slide_to_act/slide_to_act.dart';
|
||||
import 'package:vibration/vibration.dart';
|
||||
|
||||
import '../../../../constant/colors.dart';
|
||||
import '../../../../constant/style.dart';
|
||||
import '../../../../controller/home/captin/map_driver_controller.dart';
|
||||
import '../../../widgets/elevated_btn.dart';
|
||||
|
||||
GetBuilder<MapDriverController> driverEndRideBar() {
|
||||
return GetBuilder<MapDriverController>(
|
||||
builder: (mapDriverController) => mapDriverController.isRideStarted
|
||||
? Positioned(
|
||||
left: 5,
|
||||
top: 5,
|
||||
right: 5,
|
||||
child: Container(
|
||||
decoration: AppStyle.boxDecoration1.copyWith(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
blurRadius: 10,
|
||||
offset: Offset(0, 5),
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: const EdgeInsets.all(10),
|
||||
height: mapDriverController.remainingTimeTimerRideBegin < 60
|
||||
? mapDriverController.driverEndPage = 190
|
||||
: mapDriverController.carType == 'Mishwar Vip'
|
||||
? 120
|
||||
: 170,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
if (mapDriverController.carType != 'Mishwar Vip')
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
_buildInfoColumn(
|
||||
icon: Icons.social_distance,
|
||||
text: '${mapDriverController.distance} ${'KM'.tr}',
|
||||
),
|
||||
_buildInfoColumn(
|
||||
icon: Icons.timelapse,
|
||||
text: mapDriverController.hours > 1
|
||||
? '${mapDriverController.hours} ${'H and'.tr} ${mapDriverController.minutes} m'
|
||||
: '${mapDriverController.minutes} ${'m'.tr}',
|
||||
),
|
||||
_buildInfoColumn(
|
||||
icon: Icons.money_sharp,
|
||||
text:
|
||||
'${mapDriverController.paymentAmount} ${'\$'.tr}',
|
||||
),
|
||||
],
|
||||
),
|
||||
if (mapDriverController.carType != 'Speed' &&
|
||||
mapDriverController.carType != 'Awfar Car' &&
|
||||
mapDriverController.carType != 'Scooter')
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
_buildInfoBox(
|
||||
icon: Icons.timer,
|
||||
text:
|
||||
mapDriverController.stringRemainingTimeRideBegin1,
|
||||
),
|
||||
_buildInfoBox(
|
||||
icon: Icons.location_on,
|
||||
text:
|
||||
'${mapDriverController.recentDistanceToDash.toStringAsFixed(0)} ${'KM'.tr}',
|
||||
),
|
||||
_buildInfoBox(
|
||||
icon: Icons.attach_money,
|
||||
text: mapDriverController.price.toStringAsFixed(2),
|
||||
),
|
||||
],
|
||||
),
|
||||
_builtTimerAndCarType(),
|
||||
Container(
|
||||
width: Get.width * 0.8,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColor.redColor.withOpacity(0.3),
|
||||
blurRadius: 8,
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: SlideAction(
|
||||
height: 50,
|
||||
borderRadius: 15,
|
||||
elevation: 4,
|
||||
text: 'Slide to End Trip'.tr,
|
||||
textStyle: AppStyle.title.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
outerColor: AppColor.redColor,
|
||||
innerColor: Colors.white,
|
||||
sliderButtonIcon: const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
color: AppColor.redColor,
|
||||
size: 24,
|
||||
),
|
||||
sliderRotate: false,
|
||||
onSubmit: () {
|
||||
HapticFeedback.mediumImpact();
|
||||
mapDriverController.finishRideFromDriver();
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
);
|
||||
}
|
||||
|
||||
class _builtTimerAndCarType extends StatelessWidget {
|
||||
const _builtTimerAndCarType({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final mapDriverController = Get.find<MapDriverController>();
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Container(
|
||||
decoration: AppStyle.boxDecoration1.copyWith(
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColor.accentColor.withOpacity(0.2),
|
||||
blurRadius: 8,
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Text(
|
||||
mapDriverController.carType,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
if (mapDriverController.carType != 'Comfort' &&
|
||||
mapDriverController.carType != 'Mishwar Vip' &&
|
||||
mapDriverController.carType != 'Lady')
|
||||
Container(
|
||||
width: Get.width * 0.6,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
mapDriverController.remainingTimeTimerRideBegin < 60
|
||||
? AppColor.redColor.withOpacity(0.8)
|
||||
: AppColor.greenColor.withOpacity(0.8),
|
||||
mapDriverController.remainingTimeTimerRideBegin < 60
|
||||
? AppColor.redColor
|
||||
: AppColor.greenColor,
|
||||
],
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: (mapDriverController.remainingTimeTimerRideBegin < 60
|
||||
? AppColor.redColor
|
||||
: AppColor.greenColor)
|
||||
.withOpacity(0.3),
|
||||
blurRadius: 8,
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Stack(
|
||||
children: [
|
||||
LinearProgressIndicator(
|
||||
backgroundColor: Colors.white.withOpacity(0.2),
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
Colors.white.withOpacity(0.5),
|
||||
),
|
||||
minHeight: 40,
|
||||
value:
|
||||
mapDriverController.progressTimerRideBegin.toDouble(),
|
||||
),
|
||||
Center(
|
||||
child: AnimatedDefaultTextStyle(
|
||||
duration: Duration(milliseconds: 300),
|
||||
style: AppStyle.title.copyWith(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize:
|
||||
mapDriverController.remainingTimeTimerRideBegin < 60
|
||||
? 18
|
||||
: 16,
|
||||
shadows: [
|
||||
Shadow(
|
||||
color: Colors.black26,
|
||||
offset: Offset(0, 2),
|
||||
blurRadius: 4,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Text(
|
||||
mapDriverController.stringRemainingTimeRideBegin,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildInfoColumn({required IconData icon, required String text}) {
|
||||
return Column(
|
||||
children: [
|
||||
Icon(icon),
|
||||
Text(
|
||||
text,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInfoBox({required IconData icon, required String text}) {
|
||||
return Container(
|
||||
width: Get.width * .2,
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(icon),
|
||||
SizedBox(width: 4),
|
||||
Text(
|
||||
text,
|
||||
style: AppStyle.number,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
GetBuilder<MapDriverController> speedCircle() {
|
||||
if (Get.find<MapDriverController>().speed > 100) {
|
||||
if (Platform.isIOS) {
|
||||
HapticFeedback.selectionClick();
|
||||
} else {
|
||||
Vibration.vibrate(duration: 1000);
|
||||
}
|
||||
Get.defaultDialog(
|
||||
barrierDismissible: false,
|
||||
titleStyle: AppStyle.title,
|
||||
title: 'Speed Over'.tr,
|
||||
middleText: 'Please slow down'.tr,
|
||||
middleTextStyle: AppStyle.title,
|
||||
confirm: MyElevatedButton(
|
||||
title: 'I will slow down'.tr,
|
||||
onPressed: () => Get.back(),
|
||||
),
|
||||
);
|
||||
}
|
||||
return GetBuilder<MapDriverController>(
|
||||
builder: (mapDriverController) {
|
||||
return mapDriverController.isRideStarted
|
||||
? Positioned(
|
||||
bottom: 25,
|
||||
right: 100,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: mapDriverController.speed > 100
|
||||
? Colors.red
|
||||
: AppColor.secondaryColor,
|
||||
border: Border.all(width: 3, color: AppColor.redColor),
|
||||
),
|
||||
height: 60,
|
||||
width: 60,
|
||||
child: Center(
|
||||
child: Text(
|
||||
mapDriverController.speed.toStringAsFixed(0),
|
||||
style: AppStyle.number,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox();
|
||||
},
|
||||
);
|
||||
}
|
||||
108
lib/views/home/Captin/mapDriverWidgets/google_driver_map_page.dart
Executable file
108
lib/views/home/Captin/mapDriverWidgets/google_driver_map_page.dart
Executable file
@@ -0,0 +1,108 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
|
||||
import '../../../../controller/functions/location_controller.dart';
|
||||
import '../../../../controller/home/captin/map_driver_controller.dart';
|
||||
|
||||
class GoogleDriverMap extends StatelessWidget {
|
||||
const GoogleDriverMap({
|
||||
super.key,
|
||||
required this.locationController,
|
||||
});
|
||||
|
||||
final LocationController locationController;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(MapDriverController());
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: GetBuilder<MapDriverController>(
|
||||
builder: (controller) => Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: Get.height * .92,
|
||||
child: GoogleMap(
|
||||
onMapCreated: controller.onMapCreated,
|
||||
zoomControlsEnabled: true,
|
||||
initialCameraPosition: CameraPosition(
|
||||
target: locationController.myLocation,
|
||||
zoom: 13,
|
||||
bearing: locationController.heading,
|
||||
tilt: 40,
|
||||
),
|
||||
cameraTargetBounds:
|
||||
CameraTargetBounds.unbounded, // Allow unrestricted movement
|
||||
onCameraMove: (position) {
|
||||
CameraPosition(
|
||||
target: locationController.myLocation,
|
||||
zoom: 13,
|
||||
bearing: locationController.heading,
|
||||
tilt: 40,
|
||||
);
|
||||
//todo
|
||||
// locationController.myLocation = position.target;
|
||||
//
|
||||
// controller.mapController
|
||||
// ?.animateCamera(CameraUpdate.newCameraPosition(position));
|
||||
},
|
||||
minMaxZoomPreference: const MinMaxZoomPreference(8, 15),
|
||||
myLocationEnabled: true,
|
||||
myLocationButtonEnabled: true,
|
||||
compassEnabled: true,
|
||||
mapType: MapType.terrain,
|
||||
rotateGesturesEnabled: true,
|
||||
scrollGesturesEnabled: true,
|
||||
trafficEnabled: false,
|
||||
buildingsEnabled: true,
|
||||
mapToolbarEnabled: true,
|
||||
fortyFiveDegreeImageryEnabled: true,
|
||||
zoomGesturesEnabled: true,
|
||||
polylines: {
|
||||
Polyline(
|
||||
zIndex: 2,
|
||||
geodesic: true,
|
||||
polylineId: const PolylineId('route1'),
|
||||
points: controller.polylineCoordinates,
|
||||
color: const Color.fromARGB(255, 163, 81, 246),
|
||||
width: 5,
|
||||
),
|
||||
Polyline(
|
||||
zIndex: 2,
|
||||
geodesic: true,
|
||||
polylineId: const PolylineId('route'),
|
||||
points: controller.polylineCoordinatesDestination,
|
||||
color: const Color.fromARGB(255, 10, 29, 126),
|
||||
width: 5,
|
||||
),
|
||||
},
|
||||
markers: {
|
||||
Marker(
|
||||
markerId: MarkerId('MyLocation'.tr),
|
||||
position: locationController.myLocation,
|
||||
draggable: true,
|
||||
icon: controller.carIcon,
|
||||
rotation: locationController.heading,
|
||||
),
|
||||
Marker(
|
||||
markerId: MarkerId('start'.tr),
|
||||
position: controller.latLngPassengerLocation,
|
||||
draggable: true,
|
||||
icon: controller.startIcon,
|
||||
),
|
||||
Marker(
|
||||
markerId: MarkerId('end'.tr),
|
||||
position: controller.latLngPassengerDestination,
|
||||
draggable: true,
|
||||
icon: controller.endIcon,
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
54
lib/views/home/Captin/mapDriverWidgets/google_map_app.dart
Executable file
54
lib/views/home/Captin/mapDriverWidgets/google_map_app.dart
Executable file
@@ -0,0 +1,54 @@
|
||||
import 'package:sefer_driver/constant/colors.dart';
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class GoogleMapApp extends StatelessWidget {
|
||||
const GoogleMapApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MapDriverController>(
|
||||
builder: (mapDriverController) => mapDriverController.isRideStarted
|
||||
? Positioned(
|
||||
left: 150,
|
||||
bottom: 20,
|
||||
child: Container(
|
||||
decoration: AppStyle.boxDecoration,
|
||||
child: IconButton(
|
||||
onPressed: () async {
|
||||
var startLat = Get.find<MapDriverController>()
|
||||
.latLngPassengerLocation
|
||||
.latitude;
|
||||
var startLng = Get.find<MapDriverController>()
|
||||
.latLngPassengerLocation
|
||||
.longitude;
|
||||
|
||||
var endLat = Get.find<MapDriverController>()
|
||||
.latLngPassengerDestination
|
||||
.latitude;
|
||||
var endLng = Get.find<MapDriverController>()
|
||||
.latLngPassengerDestination
|
||||
.longitude;
|
||||
|
||||
String url =
|
||||
'https://www.google.com/maps/dir/$startLat,$startLng/$endLat,$endLng/&directionsmode=driving';
|
||||
if (await canLaunchUrl(Uri.parse(url))) {
|
||||
await launchUrl(Uri.parse(url));
|
||||
} else {
|
||||
throw 'Could not launch google maps';
|
||||
}
|
||||
},
|
||||
icon: const Icon(
|
||||
MaterialCommunityIcons.map_marker_radius,
|
||||
size: 45,
|
||||
color: AppColor.blueColor,
|
||||
),
|
||||
)),
|
||||
)
|
||||
: const SizedBox());
|
||||
}
|
||||
}
|
||||
407
lib/views/home/Captin/mapDriverWidgets/passenger_info_window.dart
Executable file
407
lib/views/home/Captin/mapDriverWidgets/passenger_info_window.dart
Executable file
@@ -0,0 +1,407 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:sefer_driver/views/widgets/my_textField.dart';
|
||||
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
||||
import 'package:bubble_head/bubble.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/constant/colors.dart';
|
||||
import 'package:sefer_driver/constant/info.dart';
|
||||
import 'package:sefer_driver/controller/firebase/firbase_messge.dart';
|
||||
import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart';
|
||||
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
|
||||
|
||||
import '../../../../constant/box_name.dart';
|
||||
import '../../../../constant/style.dart';
|
||||
import '../../../../controller/functions/launch.dart';
|
||||
import '../../../../main.dart';
|
||||
import '../../../../print.dart';
|
||||
|
||||
class PassengerInfoWindow extends StatelessWidget {
|
||||
const PassengerInfoWindow({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MapDriverController>(
|
||||
builder: (controller) => controller.isPassengerInfoWindow == true
|
||||
? Positioned(
|
||||
bottom: 10,
|
||||
left: 10,
|
||||
right: 10,
|
||||
child: Card(
|
||||
elevation: 5,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Go to passenger Location'.tr,
|
||||
style: AppStyle.title.copyWith(
|
||||
color: AppColor.greenColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
if (!controller.isRideBegin)
|
||||
Wrap(
|
||||
spacing: 16.0,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
controller.isSocialPressed = true;
|
||||
await controller.driverCallPassenger();
|
||||
makePhoneCall(
|
||||
controller.passengerPhone.toString());
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.phone,
|
||||
color: AppColor.blueColor,
|
||||
),
|
||||
tooltip: 'Call Passenger',
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
Get.bottomSheet(
|
||||
backgroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(20)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: _buildMessageOptions(controller),
|
||||
),
|
||||
);
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.message,
|
||||
color: AppColor.redColor,
|
||||
),
|
||||
tooltip: 'Send Message',
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
if (Platform.isAndroid) {
|
||||
Bubble().startBubbleHead(
|
||||
sendAppToBackground: true);
|
||||
}
|
||||
await controller
|
||||
.openGoogleMapFromDriverToPassenger();
|
||||
},
|
||||
icon: const Icon(
|
||||
MaterialCommunityIcons.map_marker_radius,
|
||||
size: 28,
|
||||
color: AppColor.blueColor,
|
||||
),
|
||||
tooltip: 'Open in Maps',
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
_buildInfoTile(
|
||||
icon: Icons.timer,
|
||||
text: controller.hours > 1
|
||||
? '${controller.hours}h ${controller.minutes}m'
|
||||
: '${controller.minutes}m',
|
||||
label: 'Duration',
|
||||
),
|
||||
_buildInfoTile(
|
||||
icon: Icons.map,
|
||||
text: '${controller.distance} km',
|
||||
label: 'Distance',
|
||||
),
|
||||
_buildInfoTile(
|
||||
icon: Icons.person,
|
||||
text: controller.passengerName,
|
||||
label: 'Passenger',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
_buildInfoTile(
|
||||
icon: Icons.attach_money,
|
||||
text: controller.totalPricePassenger,
|
||||
label: 'Cost',
|
||||
),
|
||||
_buildInfoTile(
|
||||
icon: Icons.directions_car,
|
||||
text: controller.carType.tr,
|
||||
label: 'Car Type',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (!controller.isRideBegin)
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Expanded(
|
||||
child: MyElevatedButton(
|
||||
title: 'Start the Ride'.tr,
|
||||
kolor: AppColor.greenColor,
|
||||
onPressed: () {
|
||||
MyDialog().getDialog(
|
||||
"Is the Passenger in your Car?".tr,
|
||||
"Don't start trip if passenger not in your car"
|
||||
.tr,
|
||||
() async {
|
||||
await controller
|
||||
.startRideFromDriver();
|
||||
Get.back();
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
if (controller.isArrivedSend)
|
||||
Expanded(
|
||||
child: MyElevatedButton(
|
||||
title: 'I Arrive'.tr,
|
||||
kolor: AppColor.yellowColor,
|
||||
onPressed: () async {
|
||||
if (await controller
|
||||
.calculateDistanceBetweenDriverAndPassengerLocation() <
|
||||
140) {
|
||||
Get.find<FirebaseMessagesController>()
|
||||
.sendNotificationToDriverMAP(
|
||||
'Hi ,I Arrive your site',
|
||||
'I Arrive at your site'.tr,
|
||||
controller.tokenPassenger,
|
||||
[],
|
||||
'ding.wav',
|
||||
);
|
||||
controller
|
||||
.startTimerToShowDriverWaitPassengerDuration();
|
||||
controller.isArrivedSend = false;
|
||||
} else {
|
||||
MyDialog().getDialog(
|
||||
'You are not near the passenger location'
|
||||
.tr,
|
||||
'Please go to the pickup location exactly'
|
||||
.tr, () {
|
||||
Get.back();
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
if (controller.remainingTimeInPassengerLocatioWait <
|
||||
300 &&
|
||||
controller
|
||||
.remainingTimeInPassengerLocatioWait !=
|
||||
0)
|
||||
Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
LinearProgressIndicator(
|
||||
backgroundColor: AppColor.greyColor,
|
||||
color: controller
|
||||
.remainingTimeInPassengerLocatioWait <
|
||||
60
|
||||
? AppColor.redColor
|
||||
: AppColor.greenColor,
|
||||
minHeight: 20,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
value: controller
|
||||
.progressInPassengerLocationFromDriver
|
||||
.toDouble(),
|
||||
),
|
||||
Text(
|
||||
controller
|
||||
.stringRemainingTimeWaitingPassenger,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
if (controller.isdriverWaitTimeEnd)
|
||||
MyElevatedButton(
|
||||
title:
|
||||
'You Can Cancel the Trip and get Cost From '
|
||||
.tr +
|
||||
AppInformation.appName.tr,
|
||||
kolor: AppColor.deepPurpleAccent,
|
||||
onPressed: () {
|
||||
MyDialog().getDialog(
|
||||
'Are you sure to cancel?'.tr, '',
|
||||
() async {
|
||||
Get.find<FirebaseMessagesController>()
|
||||
.sendNotificationToDriverMAP(
|
||||
'Driver Cancelled Your Trip',
|
||||
'You will need to pay the cost to the driver, or it will be deducted from your next trip'
|
||||
.tr,
|
||||
controller.tokenPassenger,
|
||||
[],
|
||||
'cancel.wav',
|
||||
);
|
||||
Log.print(
|
||||
'rideStatus from passenge info 261 : ${box.read(BoxName.rideStatus)}');
|
||||
box.write(BoxName.rideStatus, 'Cancel');
|
||||
await controller
|
||||
.addWaitingTimeCostFromPassengerToDriverWallet();
|
||||
controller.isdriverWaitTimeEnd = false;
|
||||
Get.back();
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInfoTile({
|
||||
required IconData icon,
|
||||
required String text,
|
||||
required String label,
|
||||
}) {
|
||||
return Column(
|
||||
children: [
|
||||
Icon(icon, color: Colors.grey[700]),
|
||||
const SizedBox(height: 4),
|
||||
Text(text, style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
|
||||
Text(label.tr, style: AppStyle.title),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMessageOptions(MapDriverController controller) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text('Select a quick message'.tr, style: AppStyle.title),
|
||||
const SizedBox(height: 16),
|
||||
_buildMessageTile(
|
||||
text: "Where are you, sir?".tr,
|
||||
onTap: () {
|
||||
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
|
||||
'message From Driver',
|
||||
"Where are you, sir?".tr,
|
||||
controller.tokenPassenger,
|
||||
[],
|
||||
'ding.wav',
|
||||
);
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
_buildMessageTile(
|
||||
text: "I've been trying to reach you but your phone is off.".tr,
|
||||
onTap: () {
|
||||
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
|
||||
'message From Driver',
|
||||
"I've been trying to reach you but your phone is off.".tr,
|
||||
controller.tokenPassenger,
|
||||
[],
|
||||
'ding.wav',
|
||||
);
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
_buildMessageTile(
|
||||
text:
|
||||
"Please don't be late, I'm waiting for you at the specified location."
|
||||
.tr,
|
||||
onTap: () {
|
||||
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
|
||||
'message From Driver',
|
||||
"Please don't be late, I'm waiting for you at the specified location."
|
||||
.tr,
|
||||
controller.tokenPassenger,
|
||||
[],
|
||||
'ding.wav',
|
||||
);
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
_buildMessageTile(
|
||||
text: "Please don't be late".tr,
|
||||
onTap: () {
|
||||
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
|
||||
'message From Driver',
|
||||
"Please don't be late".tr,
|
||||
controller.tokenPassenger,
|
||||
[],
|
||||
'cancel.wav',
|
||||
);
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Form(
|
||||
key: controller.formKey2,
|
||||
child: MyTextForm(
|
||||
controller: controller.messageToPassenger,
|
||||
label: 'Type something'.tr,
|
||||
hint: 'Type something'.tr,
|
||||
type: TextInputType.text,
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
Get.find<FirebaseMessagesController>()
|
||||
.sendNotificationToDriverMAP(
|
||||
'message From Driver',
|
||||
controller.messageToPassenger.text,
|
||||
controller.tokenPassenger,
|
||||
[],
|
||||
'ding.wav',
|
||||
);
|
||||
controller.messageToPassenger.clear();
|
||||
Get.back();
|
||||
},
|
||||
icon: const Icon(Icons.send),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMessageTile(
|
||||
{required String text, required VoidCallback onTap}) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
margin: const EdgeInsets.symmetric(vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: Colors.grey[100],
|
||||
),
|
||||
child: Text(text, style: AppStyle.title),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
193
lib/views/home/Captin/mapDriverWidgets/sos_connect.dart
Executable file
193
lib/views/home/Captin/mapDriverWidgets/sos_connect.dart
Executable file
@@ -0,0 +1,193 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:bubble_head/bubble.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/constant/info.dart';
|
||||
import 'package:sefer_driver/controller/functions/location_controller.dart';
|
||||
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
|
||||
import 'package:sefer_driver/views/widgets/my_textField.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../../../../constant/box_name.dart';
|
||||
import '../../../../constant/colors.dart';
|
||||
import '../../../../constant/style.dart';
|
||||
import '../../../../controller/functions/launch.dart';
|
||||
import '../../../../controller/home/captin/map_driver_controller.dart';
|
||||
import '../../../../main.dart';
|
||||
|
||||
class SosConnect extends StatelessWidget {
|
||||
const SosConnect({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MapDriverController>(
|
||||
builder: (mapDriverController) => mapDriverController.isRideStarted
|
||||
? Positioned(
|
||||
left: 16,
|
||||
bottom: 16,
|
||||
child: Card(
|
||||
elevation: 4,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: SizedBox(
|
||||
height: 60,
|
||||
width: 180,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
_handleSosCall(mapDriverController);
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.sos_sharp,
|
||||
size: 32,
|
||||
color: AppColor.redColor,
|
||||
),
|
||||
tooltip: 'SOS - Call Emergency',
|
||||
),
|
||||
VerticalDivider(
|
||||
color: Colors.grey[300],
|
||||
thickness: 1,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
_handleWhatsApp(mapDriverController);
|
||||
},
|
||||
icon: const Icon(
|
||||
FontAwesome.whatsapp,
|
||||
color: AppColor.greenColor,
|
||||
size: 32,
|
||||
),
|
||||
tooltip: 'SOS - Send WhatsApp Message',
|
||||
),
|
||||
VerticalDivider(
|
||||
color: Colors.grey[300],
|
||||
thickness: 1,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
_handleGoogleMap(mapDriverController);
|
||||
},
|
||||
icon: const Icon(
|
||||
MaterialCommunityIcons.map_marker_radius,
|
||||
color: AppColor.primaryColor,
|
||||
size: 32,
|
||||
),
|
||||
tooltip: 'Google Maps - Navigate',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleSosCall(MapDriverController mapDriverController) {
|
||||
if (box.read(BoxName.sosPhoneDriver) == null) {
|
||||
Get.defaultDialog(
|
||||
title: 'Insert Emergency Number'.tr,
|
||||
content: Form(
|
||||
key: mapDriverController.formKey1,
|
||||
child: MyTextForm(
|
||||
controller: mapDriverController.sosEmergincyNumberCotroller,
|
||||
label: 'Emergency Number'.tr,
|
||||
hint: 'Enter phone number'.tr,
|
||||
type: TextInputType.phone,
|
||||
),
|
||||
),
|
||||
confirm: MyElevatedButton(
|
||||
title: 'Save'.tr,
|
||||
onPressed: () {
|
||||
if (mapDriverController.formKey1.currentState!.validate()) {
|
||||
box.write(BoxName.sosPhoneDriver,
|
||||
mapDriverController.sosEmergincyNumberCotroller.text);
|
||||
Get.back(); // Close the dialog
|
||||
launchCommunication(
|
||||
'phone', box.read(BoxName.sosPhoneDriver), '');
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
} else {
|
||||
launchCommunication('phone', box.read(BoxName.sosPhoneDriver), '');
|
||||
}
|
||||
}
|
||||
|
||||
void _handleWhatsApp(MapDriverController mapDriverController) {
|
||||
if (box.read(BoxName.sosPhoneDriver) == null) {
|
||||
Get.defaultDialog(
|
||||
title: 'Insert Emergency Number'.tr,
|
||||
content: Form(
|
||||
key: mapDriverController.formKey1,
|
||||
child: MyTextForm(
|
||||
controller: mapDriverController.sosEmergincyNumberCotroller,
|
||||
label: 'Emergency Number'.tr,
|
||||
hint: 'Enter phone number'.tr,
|
||||
type: TextInputType.phone,
|
||||
),
|
||||
),
|
||||
confirm: MyElevatedButton(
|
||||
title: 'Save'.tr,
|
||||
onPressed: () {
|
||||
if (mapDriverController.formKey1.currentState!.validate()) {
|
||||
box.write(BoxName.sosPhoneDriver,
|
||||
mapDriverController.sosEmergincyNumberCotroller.text);
|
||||
Get.back(); // Close the dialog
|
||||
_sendWhatsAppMessage(mapDriverController);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
} else {
|
||||
_sendWhatsAppMessage(mapDriverController);
|
||||
}
|
||||
}
|
||||
|
||||
void _handleGoogleMap(MapDriverController mapDriverController) {
|
||||
() async {
|
||||
if (Platform.isAndroid) {
|
||||
Bubble().startBubbleHead(sendAppToBackground: true);
|
||||
}
|
||||
var startLat =
|
||||
Get.find<MapDriverController>().latLngPassengerLocation.latitude;
|
||||
var startLng =
|
||||
Get.find<MapDriverController>().latLngPassengerLocation.longitude;
|
||||
|
||||
var endLat =
|
||||
Get.find<MapDriverController>().latLngPassengerDestination.latitude;
|
||||
var endLng =
|
||||
Get.find<MapDriverController>().latLngPassengerDestination.longitude;
|
||||
|
||||
String url =
|
||||
'https://www.google.com/maps/dir/$startLat,$startLng/$endLat,$endLng/&directionsmode=driving';
|
||||
if (await canLaunchUrl(Uri.parse(url))) {
|
||||
await launchUrl(Uri.parse(url));
|
||||
} else {
|
||||
throw 'Could not launch google maps';
|
||||
}
|
||||
}();
|
||||
}
|
||||
|
||||
void _sendWhatsAppMessage(MapDriverController mapDriverController) {
|
||||
final sosNumber = box.read(BoxName.sosPhoneDriver);
|
||||
if (sosNumber != null) {
|
||||
launchCommunication(
|
||||
'whatsapp',
|
||||
'+2$sosNumber', // Consider international format
|
||||
"${"Hello, this is Driver".tr} ${box.read(BoxName.nameDriver)}. "
|
||||
"${"My current location is:".tr} "
|
||||
"https://www.google.com/maps/place/"
|
||||
"${Get.find<LocationController>().myLocation.latitude},"
|
||||
"${Get.find<LocationController>().myLocation.longitude} "
|
||||
"${"\nI have a trip on".tr} ${AppInformation.appName} "
|
||||
"${"app with passenger".tr} ${mapDriverController.passengerName}.",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
lib/views/home/Captin/orderCaptin/call.dart
Executable file
25
lib/views/home/Captin/orderCaptin/call.dart
Executable file
@@ -0,0 +1,25 @@
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:ride/constant/api_key.dart';
|
||||
// import 'package:ride/constant/box_name.dart';
|
||||
// import 'package:ride/main.dart';
|
||||
// import 'package:zego_uikit_prebuilt_call/zego_uikit_prebuilt_call.dart';
|
||||
|
||||
// class CallPage extends StatelessWidget {
|
||||
// const CallPage({Key? key, required this.callID}) : super(key: key);
|
||||
// final String callID;
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return ZegoUIKitPrebuiltCall(
|
||||
// appID: AK
|
||||
// .zegoCloudAppID, // Fill in the appID that you get from ZEGOCLOUD Admin Console.
|
||||
// appSign: AK
|
||||
// .zegoCloudAppSIGN, // Fill in the appSign that you get from ZEGOCLOUD Admin Console.
|
||||
// userID: box.read(BoxName.passengerID) ?? box.read(BoxName.driverID),
|
||||
// userName: box.read(BoxName.name) ?? box.read(BoxName.nameDriver),
|
||||
// callID: callID,
|
||||
// // You can also use groupVideo/groupVoice/oneOnOneVoice to make more types of calls.
|
||||
// config: ZegoUIKitPrebuiltCallConfig.oneOnOneVoiceCall(),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
811
lib/views/home/Captin/orderCaptin/order_over_lay.dart
Executable file
811
lib/views/home/Captin/orderCaptin/order_over_lay.dart
Executable file
@@ -0,0 +1,811 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_overlay_window/flutter_overlay_window.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
import 'package:sefer_driver/constant/api_key.dart';
|
||||
import '../../../../constant/box_name.dart';
|
||||
import '../../../../constant/links.dart';
|
||||
import '../../../../controller/firebase/local_notification.dart';
|
||||
import '../../../../controller/functions/crud.dart';
|
||||
import '../../../../main.dart';
|
||||
import '../../../../models/model/order_data.dart';
|
||||
import '../../../../print.dart';
|
||||
|
||||
// === Enhanced Colors for Better Readability ===
|
||||
class AppColors {
|
||||
static const primary = Color(0xFF1A252F);
|
||||
static const card = Color(0xFF2C3E50);
|
||||
static const white = Colors.white;
|
||||
static const gray = Color(0xFFBDC3C7);
|
||||
static const lightGray = Color(0xFFECF0F1);
|
||||
static const accent = Color(0xFF00BCD4);
|
||||
static const accept = Color(0xFF4CAF50);
|
||||
static const reject = Color(0xFFFF5722);
|
||||
static const highlight = Color(0xFFFFC107);
|
||||
static const priceHighlight = Color(0xFF00E676);
|
||||
static const urgentRed = Color(0xFFD32F2F);
|
||||
}
|
||||
|
||||
class OrderOverlay extends StatefulWidget {
|
||||
const OrderOverlay({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<OrderOverlay> createState() => _OrderOverlayState();
|
||||
}
|
||||
|
||||
class _OrderOverlayState extends State<OrderOverlay>
|
||||
with WidgetsBindingObserver {
|
||||
// === State Variables ===
|
||||
OrderData? orderData;
|
||||
Timer? timer;
|
||||
int remainingSeconds = 10;
|
||||
final AudioPlayer audioPlayer = AudioPlayer();
|
||||
bool buttonsEnabled = true;
|
||||
final String mapApiKey = AK.mapAPIKEY;
|
||||
final CRUD _crud = CRUD();
|
||||
|
||||
final NotificationController notificationController =
|
||||
Get.put(NotificationController());
|
||||
// === Getters ===
|
||||
bool get canShowMap {
|
||||
if (orderData == null || mapApiKey.isEmpty) return false;
|
||||
final start = orderData!.startCoordinates;
|
||||
final end = orderData!.endCoordinates;
|
||||
return start?['lat'] != null &&
|
||||
start?['lng'] != null &&
|
||||
end?['lat'] != null &&
|
||||
end?['lng'] != null;
|
||||
}
|
||||
|
||||
String get staticMapUrl {
|
||||
if (!canShowMap) return "";
|
||||
final start = orderData!.startCoordinates!;
|
||||
final end = orderData!.endCoordinates!;
|
||||
final startMarker = Uri.encodeComponent("${start['lat']},${start['lng']}");
|
||||
final endMarker = Uri.encodeComponent("${end['lat']},${end['lng']}");
|
||||
|
||||
return "https://maps.googleapis.com/maps/api/staticmap?"
|
||||
"size=600x150&maptype=roadmap"
|
||||
"&markers=color:green%7Clabel:S%7C$startMarker"
|
||||
"&markers=color:red%7Clabel:D%7C$endMarker"
|
||||
"&path=color:0x007bff%7Cweight:5%7C$startMarker%7C$endMarker"
|
||||
"&key=$mapApiKey";
|
||||
}
|
||||
|
||||
// === Lifecycle ===
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
FlutterOverlayWindow.overlayListener.listen((event) {
|
||||
if (mounted) _processEventData(event);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
timer?.cancel();
|
||||
_stopAudio();
|
||||
audioPlayer.dispose();
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
_checkOverlayStatus();
|
||||
}
|
||||
}
|
||||
|
||||
List myList = [];
|
||||
// === Setup & Listeners ===
|
||||
void _setupOverlayListener() {
|
||||
FlutterOverlayWindow.overlayListener.listen((event) {
|
||||
if (mounted) _processEventData(event);
|
||||
});
|
||||
}
|
||||
|
||||
void _processEventData(dynamic event) {
|
||||
_log("Received event: $event");
|
||||
if (event is List<dynamic>) {
|
||||
try {
|
||||
myList = event;
|
||||
final newOrder = OrderData.fromList(event);
|
||||
_log("Parsed OrderData: ${newOrder.toMap()}");
|
||||
setState(() {
|
||||
orderData = newOrder;
|
||||
});
|
||||
_resetAndStartTimer();
|
||||
} catch (e, s) {
|
||||
_log("Error parsing OrderData: $e\nStackTrace: $s");
|
||||
}
|
||||
} else {
|
||||
_log("Unexpected data format: $event");
|
||||
}
|
||||
}
|
||||
|
||||
void _checkOverlayStatus() async {
|
||||
bool isActive = await FlutterOverlayWindow.isActive();
|
||||
if (isActive && mounted && orderData != null) {
|
||||
if (remainingSeconds > 0 && (timer == null || !timer!.isActive)) {
|
||||
_resetAndStartTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === Timer Management ===
|
||||
void _resetAndStartTimer() {
|
||||
timer?.cancel();
|
||||
audioPlayer.stop();
|
||||
setState(() {
|
||||
buttonsEnabled = true;
|
||||
remainingSeconds = _calculateTimerDuration();
|
||||
});
|
||||
_playAudio();
|
||||
_startTimer();
|
||||
}
|
||||
|
||||
int _calculateTimerDuration() {
|
||||
if (orderData?.durationToPassengerMinutes != null &&
|
||||
orderData!.durationToPassengerMinutes > 0) {
|
||||
int duration = orderData!.durationToPassengerMinutes * 60;
|
||||
return duration > 10 ? 10 : duration;
|
||||
}
|
||||
return 10;
|
||||
}
|
||||
|
||||
void _startTimer() {
|
||||
if (orderData == null) return;
|
||||
timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
if (!mounted) {
|
||||
timer.cancel();
|
||||
_stopAudio();
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
if (remainingSeconds > 0) {
|
||||
remainingSeconds--;
|
||||
} else {
|
||||
timer.cancel();
|
||||
_stopAudio();
|
||||
if (buttonsEnabled) _handleOrderTimeout();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// === Audio Management ===
|
||||
void _playAudio() async {
|
||||
try {
|
||||
await audioPlayer.setAsset('assets/order.mp3', preload: true);
|
||||
await audioPlayer.setLoopMode(LoopMode.one);
|
||||
await audioPlayer.play();
|
||||
} catch (e) {
|
||||
_log('Error playing audio: $e');
|
||||
}
|
||||
}
|
||||
|
||||
void _stopAudio() {
|
||||
audioPlayer.stop();
|
||||
}
|
||||
|
||||
String _getData(int index, {String defaultValue = ''}) {
|
||||
if (myList.length > index && myList[index] != null) {
|
||||
return myList[index].toString();
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
// === Order Actions ===
|
||||
Future<void> _acceptOrder() async {
|
||||
if (!buttonsEnabled || orderData == null) return;
|
||||
_disableButtonsAndProcess();
|
||||
_log("Order ACCEPTED: ${orderData!.orderId}");
|
||||
|
||||
try {
|
||||
final driverId = box.read(BoxName.driverID)?.toString();
|
||||
if (driverId == null) {
|
||||
_log("Error: Driver ID is null. Closing overlay.");
|
||||
await _closeOverlay();
|
||||
return;
|
||||
}
|
||||
|
||||
var res = await CRUD().post(link: AppLink.updateStausFromSpeed, payload: {
|
||||
'id': orderData!.orderId,
|
||||
'rideTimeStart': DateTime.now().toString(),
|
||||
'status': 'Apply',
|
||||
'driver_id': box.read(BoxName.driverID),
|
||||
});
|
||||
if (AppLink.endPoint != AppLink.seferCairoServer) {
|
||||
CRUD().post(
|
||||
link: "${AppLink.endPoint}/ride/rides/updateStausFromSpeed.php",
|
||||
payload: {
|
||||
'id': orderData!.orderId,
|
||||
'rideTimeStart': DateTime.now().toString(),
|
||||
'status': 'Apply',
|
||||
'driver_id': box.read(BoxName.driverID),
|
||||
});
|
||||
}
|
||||
final payload = {
|
||||
// بيانات أساسية
|
||||
'driver_id': driverId,
|
||||
'status': 'Apply',
|
||||
'passengerLocation': _getData(0),
|
||||
'passengerDestination': _getData(1),
|
||||
'Duration': _getData(4),
|
||||
'totalCost': _getData(26),
|
||||
'Distance': _getData(5),
|
||||
'name': _getData(8),
|
||||
'phone': _getData(10),
|
||||
'email': _getData(28),
|
||||
'WalletChecked': _getData(13),
|
||||
'tokenPassenger': _getData(9),
|
||||
'direction': staticMapUrl.toString(),
|
||||
'DurationToPassenger': _getData(15),
|
||||
'rideId': orderData!.orderId,
|
||||
'passengerId': _getData(7),
|
||||
'durationOfRideValue': _getData(19),
|
||||
'paymentAmount': _getData(2),
|
||||
'paymentMethod': _getData(13) == 'true' ? 'visa' : 'cash',
|
||||
'isHaveSteps': _getData(20),
|
||||
'step0': myList[21].toString(),
|
||||
'step1': myList[22].toString(),
|
||||
'step2': myList[23].toString(),
|
||||
'step3': myList[24].toString(),
|
||||
'step4': myList[25].toString(),
|
||||
'passengerWalletBurc': myList[26].toString(),
|
||||
'carType': myList[31].toString(),
|
||||
'kazan': myList[32].toString(),
|
||||
'startNameLocation': myList[29].toString(),
|
||||
'endNameLocation': myList[30].toString(),
|
||||
// الحقول الإضافية التي يجب تضمينها
|
||||
'timeOfOrder': DateTime.now().toIso8601String(),
|
||||
'totalPassenger': _getData(2),
|
||||
};
|
||||
Log.print('myList: ${myList}');
|
||||
Log.print('payload: ${payload}');
|
||||
CRUD().post(
|
||||
link: AppLink.addOverLayStatus,
|
||||
payload: payload,
|
||||
);
|
||||
if (res != "failure") {
|
||||
// Using rideId (_getData(16)) for order_id consistently
|
||||
CRUD().post(link: AppLink.addDriverOrder, payload: {
|
||||
'driver_id': driverId, // Driver ID from the order data
|
||||
'order_id': orderData!.orderId,
|
||||
'status': 'Apply'
|
||||
});
|
||||
|
||||
if (AppLink.endPoint != AppLink.seferCairoServer) {
|
||||
CRUD().post(
|
||||
link: "${AppLink.endPoint}/ride/driver_order/add.php",
|
||||
payload: {
|
||||
'driver_id': driverId,
|
||||
'order_id': orderData!.orderId,
|
||||
'status': 'Apply'
|
||||
});
|
||||
}
|
||||
_log("Server update successful. Writing to storage.");
|
||||
notificationController.showNotification(
|
||||
"Order Accepted".tr,
|
||||
"Open app and go to passenger".tr,
|
||||
'ding',
|
||||
'',
|
||||
);
|
||||
await _closeOverlay();
|
||||
} else {
|
||||
_log("Failed to update order status on server: $res");
|
||||
notificationController.showNotification(
|
||||
"Order Accepted by another driver".tr,
|
||||
"Open app and go to passenger".tr,
|
||||
'ding',
|
||||
'',
|
||||
);
|
||||
await _closeOverlay();
|
||||
}
|
||||
} catch (e, s) {
|
||||
_log(
|
||||
"A critical error occurred during server update: $e\nStackTrace: $s");
|
||||
if (mounted) setState(() => buttonsEnabled = true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Your list parsing for 'customerToken' should be something like:
|
||||
// customerToken: list.length > a_certain_index ? list[a_certain_index].toString() : null,
|
||||
Future<void> _rejectOrder() async {
|
||||
if (!buttonsEnabled || orderData == null) return;
|
||||
_disableButtonsAndProcess();
|
||||
_log("Order REJECTED: ${orderData!.orderId}");
|
||||
box.write(BoxName.rideStatus, 'reject');
|
||||
Log.print('rideStatus from overlay 303 : ${box.read(BoxName.rideStatus)}');
|
||||
await _apiRefuseOrder(orderData!.orderId);
|
||||
await _closeOverlay();
|
||||
}
|
||||
|
||||
void _handleOrderTimeout() {
|
||||
if (orderData == null) return;
|
||||
_log("Order TIMED OUT: ${orderData!.orderId}");
|
||||
_rejectOrder();
|
||||
}
|
||||
|
||||
Future<void> _apiRefuseOrder(String orderID) async {
|
||||
if (orderID == "N/A") {
|
||||
_log("Cannot refuse order with N/A ID");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final driverId = box.read(BoxName.driverID)?.toString();
|
||||
if (driverId == null) {
|
||||
_log("Driver ID is null, cannot refuse order");
|
||||
return;
|
||||
}
|
||||
await _crud.post(link: AppLink.addDriverOrder, payload: {
|
||||
'driver_id': driverId,
|
||||
'order_id': orderID,
|
||||
'status': 'Refused'
|
||||
});
|
||||
await _crud.post(link: AppLink.updateRides, payload: {
|
||||
'id': orderID,
|
||||
'status': 'Refused',
|
||||
'driver_id': driverId,
|
||||
});
|
||||
_log("Order $orderID refused successfully");
|
||||
} catch (e) {
|
||||
_log("Error in _apiRefuseOrder for $orderID: $e");
|
||||
}
|
||||
}
|
||||
|
||||
// === Helper Methods ===
|
||||
void _disableButtonsAndProcess() {
|
||||
setState(() => buttonsEnabled = false);
|
||||
timer?.cancel();
|
||||
_stopAudio();
|
||||
}
|
||||
|
||||
Future<void> _closeOverlay() async {
|
||||
_stopAudio();
|
||||
timer?.cancel();
|
||||
if (await FlutterOverlayWindow.isActive()) {
|
||||
await FlutterOverlayWindow.closeOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
void _log(String message) {
|
||||
// A simple logger to distinguish overlay logs
|
||||
print("OVERLAY_LOG: $message");
|
||||
}
|
||||
|
||||
// === UI Build Methods ===
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// ... (Your entire UI build method remains unchanged) ...
|
||||
// The UI code is excellent and doesn't need modification.
|
||||
if (orderData == null) {
|
||||
return const Material(
|
||||
color: Colors.transparent,
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(color: AppColors.accent)));
|
||||
}
|
||||
|
||||
return Material(
|
||||
color: Colors.black.withOpacity(0.4),
|
||||
child: Center(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.card,
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
border: Border.all(
|
||||
color: AppColors.accent.withOpacity(0.3), width: 1.5),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.6),
|
||||
blurRadius: 15,
|
||||
spreadRadius: 2,
|
||||
)
|
||||
],
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildQuickHeader(),
|
||||
const SizedBox(height: 12),
|
||||
_buildPrimaryInfo(),
|
||||
const SizedBox(height: 12),
|
||||
if (canShowMap) _buildCompactMap(),
|
||||
if (canShowMap) const SizedBox(height: 12),
|
||||
_buildSecondaryInfo(),
|
||||
const SizedBox(height: 16),
|
||||
_buildEnhancedActionButtons(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// All your _build... widget methods (_buildQuickHeader, _buildPrimaryInfo, etc.)
|
||||
// are perfectly fine and do not need to be changed.
|
||||
// ... Paste all your existing _build... methods here ...
|
||||
Widget _buildQuickHeader() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
remainingSeconds <= 3
|
||||
? AppColors.urgentRed
|
||||
: remainingSeconds <= 5
|
||||
? AppColors.highlight
|
||||
: AppColors.accent,
|
||||
remainingSeconds <= 3
|
||||
? AppColors.urgentRed.withOpacity(0.7)
|
||||
: remainingSeconds <= 5
|
||||
? AppColors.highlight.withOpacity(0.7)
|
||||
: AppColors.accent.withOpacity(0.7),
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.drive_eta_rounded, color: AppColors.white, size: 24),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
"طلب جديد".tr,
|
||||
style: const TextStyle(
|
||||
color: AppColors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600),
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.white.withOpacity(0.9),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Text(
|
||||
"$remainingSeconds ث",
|
||||
style: TextStyle(
|
||||
color: remainingSeconds <= 3
|
||||
? AppColors.urgentRed
|
||||
: remainingSeconds <= 5
|
||||
? AppColors.highlight
|
||||
: AppColors.accent,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPrimaryInfo() {
|
||||
final order = orderData!;
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primary.withOpacity(0.6),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: AppColors.priceHighlight.withOpacity(0.3), width: 1),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
// Price and Distance - Most Important Info
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: _buildHighlightInfo("\$${order.price}", "السعر".tr,
|
||||
Icons.monetization_on_rounded, AppColors.priceHighlight,
|
||||
isLarge: true),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: _buildHighlightInfo(
|
||||
"${order.tripDistanceKm.toStringAsFixed(1)} كم",
|
||||
"المسافة".tr,
|
||||
Icons.straighten_rounded,
|
||||
AppColors.accent,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Divider(color: AppColors.gray.withOpacity(0.2), thickness: 1),
|
||||
const SizedBox(height: 12),
|
||||
// Passenger Info and ETA
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: _buildPassengerQuickInfo(),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: _buildHighlightInfo(
|
||||
"${order.durationToPassengerMinutes} د",
|
||||
"للوصول".tr,
|
||||
Icons.access_time_filled_rounded,
|
||||
order.durationToPassengerMinutes <= 3
|
||||
? AppColors.priceHighlight
|
||||
: AppColors.gray,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHighlightInfo(
|
||||
String value, String label, IconData icon, Color color,
|
||||
{bool isLarge = false}) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: color.withOpacity(0.3), width: 1),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Icon(icon, color: color, size: isLarge ? 24 : 20),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
value,
|
||||
style: TextStyle(
|
||||
color: AppColors.white,
|
||||
fontSize: isLarge ? 20 : 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: AppColors.lightGray.withOpacity(0.7),
|
||||
fontSize: 11,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPassengerQuickInfo() {
|
||||
final order = orderData!;
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.highlight.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border:
|
||||
Border.all(color: AppColors.highlight.withOpacity(0.3), width: 1),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.person_rounded, color: AppColors.highlight, size: 18),
|
||||
const SizedBox(width: 4),
|
||||
Expanded(
|
||||
child: Text(
|
||||
order.customerName,
|
||||
style: const TextStyle(
|
||||
color: AppColors.white,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
order.rideType,
|
||||
style: TextStyle(
|
||||
color: AppColors.lightGray.withOpacity(0.7),
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCompactMap() {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
child: Image.network(
|
||||
staticMapUrl,
|
||||
height: 100, // Reduced from 110
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Container(
|
||||
height: 100,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primary.withOpacity(0.3),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: const Center(
|
||||
child:
|
||||
Icon(Icons.map_outlined, color: AppColors.gray, size: 32)),
|
||||
);
|
||||
},
|
||||
loadingBuilder: (context, child, loadingProgress) {
|
||||
if (loadingProgress == null) return child;
|
||||
return SizedBox(
|
||||
height: 100,
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(
|
||||
value: loadingProgress.expectedTotalBytes != null
|
||||
? loadingProgress.cumulativeBytesLoaded /
|
||||
loadingProgress.expectedTotalBytes!
|
||||
: null,
|
||||
color: AppColors.accent,
|
||||
strokeWidth: 2.0,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSecondaryInfo() {
|
||||
final order = orderData!;
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primary.withOpacity(0.4),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildLocationRow(
|
||||
Icons.trip_origin_rounded,
|
||||
"من".tr,
|
||||
order.startLocationAddress,
|
||||
Colors.green.shade300,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildLocationRow(
|
||||
Icons.flag_rounded,
|
||||
"إلى".tr,
|
||||
order.endLocationAddress,
|
||||
Colors.red.shade300,
|
||||
),
|
||||
if (order.tripDurationMinutes > 0) ...[
|
||||
const SizedBox(height: 8),
|
||||
Divider(color: AppColors.gray.withOpacity(0.2), thickness: 0.5),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.timer_outlined, color: AppColors.accent, size: 16),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
"مدة الرحلة: ${order.tripDurationMinutes} دقيقة".tr,
|
||||
style: const TextStyle(
|
||||
color: AppColors.lightGray,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLocationRow(
|
||||
IconData icon, String label, String address, Color iconColor) {
|
||||
return Row(
|
||||
children: [
|
||||
Icon(icon, color: iconColor, size: 16),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
"$label: ",
|
||||
style: TextStyle(
|
||||
color: AppColors.lightGray.withOpacity(0.8),
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
address,
|
||||
style: const TextStyle(
|
||||
color: AppColors.white,
|
||||
fontSize: 12,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEnhancedActionButtons() {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: buttonsEnabled ? _rejectOrder : null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.reject,
|
||||
foregroundColor: AppColors.white,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12)),
|
||||
disabledBackgroundColor: AppColors.reject.withOpacity(0.3),
|
||||
elevation: 3,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.close_rounded, size: 20),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
"رفض".tr,
|
||||
style: const TextStyle(
|
||||
fontSize: 16, fontWeight: FontWeight.w600),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: buttonsEnabled ? _acceptOrder : null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.accept,
|
||||
foregroundColor: AppColors.white,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12)),
|
||||
disabledBackgroundColor: AppColors.accept.withOpacity(0.3),
|
||||
elevation: 3,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.check_circle_rounded, size: 20),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
"قبول".tr,
|
||||
style: const TextStyle(
|
||||
fontSize: 16, fontWeight: FontWeight.w600),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
431
lib/views/home/Captin/orderCaptin/order_request_page.dart
Executable file
431
lib/views/home/Captin/orderCaptin/order_request_page.dart
Executable file
@@ -0,0 +1,431 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart';
|
||||
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/constant/box_name.dart';
|
||||
import 'package:sefer_driver/controller/firebase/firbase_messge.dart';
|
||||
import 'package:sefer_driver/main.dart';
|
||||
import 'package:sefer_driver/views/home/Captin/driver_map_page.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'dart:math' as math;
|
||||
import '../../../../constant/colors.dart';
|
||||
import '../../../../constant/links.dart';
|
||||
import '../../../../constant/style.dart';
|
||||
import '../../../../controller/functions/crud.dart';
|
||||
import '../../../../controller/functions/encrypt_decrypt.dart';
|
||||
import '../../../../controller/functions/launch.dart';
|
||||
import '../../../../controller/home/captin/order_request_controller.dart';
|
||||
import '../../../widgets/elevated_btn.dart';
|
||||
|
||||
class OrderRequestPage extends StatefulWidget {
|
||||
const OrderRequestPage({super.key});
|
||||
|
||||
@override
|
||||
State<OrderRequestPage> createState() => _OrderRequestPageState();
|
||||
}
|
||||
|
||||
class _OrderRequestPageState extends State<OrderRequestPage> {
|
||||
final OrderRequestController orderRequestController =
|
||||
Get.put(OrderRequestController());
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Order Request'.tr),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: GetBuilder<OrderRequestController>(
|
||||
builder: (controller) {
|
||||
if (controller.myList == null) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
return Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: Get.height * 0.3,
|
||||
child: GoogleMap(
|
||||
mapType: MapType.normal,
|
||||
initialCameraPosition: CameraPosition(
|
||||
target: LatLng(controller.latPassengerLocation,
|
||||
controller.lngPassengerLocation),
|
||||
zoom: 14.0,
|
||||
),
|
||||
myLocationButtonEnabled: true,
|
||||
onMapCreated: controller.onMapCreated,
|
||||
myLocationEnabled: true,
|
||||
markers: {
|
||||
Marker(
|
||||
markerId: const MarkerId('startLocation'),
|
||||
position: LatLng(controller.latPassengerLocation,
|
||||
controller.lngPassengerLocation),
|
||||
icon: controller.startIcon,
|
||||
),
|
||||
Marker(
|
||||
markerId: const MarkerId('destinationLocation'),
|
||||
position: LatLng(controller.latPassengerDestination,
|
||||
controller.lngPassengerDestination),
|
||||
icon: controller.endIcon,
|
||||
),
|
||||
},
|
||||
polylines: {
|
||||
Polyline(
|
||||
polylineId: const PolylineId('route'),
|
||||
color: AppColor.primaryColor,
|
||||
width: 5,
|
||||
points: controller.pointsDirection,
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
Card(
|
||||
elevation: 4,
|
||||
child: ListTile(
|
||||
leading: Icon(
|
||||
controller.myList[13].toString() == 'true'
|
||||
? Icons.credit_card
|
||||
: Icons.money,
|
||||
color: controller.myList[13].toString() == 'true'
|
||||
? AppColor.deepPurpleAccent
|
||||
: AppColor.greenColor,
|
||||
),
|
||||
title: Text(
|
||||
'Payment Method'.tr,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
trailing: Text(
|
||||
controller.myList[13].toString() == 'true'
|
||||
? 'Visa'
|
||||
: 'Cash',
|
||||
style:
|
||||
Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Card(
|
||||
elevation: 4,
|
||||
child: ListTile(
|
||||
leading: const Icon(Icons.account_circle,
|
||||
color: AppColor.secondaryColor),
|
||||
title: Text(
|
||||
controller.myList[8],
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
subtitle: Row(
|
||||
children: [
|
||||
const Icon(Icons.star,
|
||||
size: 16, color: Colors.amber),
|
||||
Text(
|
||||
controller.myList[33].toString(),
|
||||
style: const TextStyle(color: Colors.amber),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Card(
|
||||
elevation: 4,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.location_on,
|
||||
color: AppColor.greenColor),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
// Keep Expanded here for layout
|
||||
child: Text(
|
||||
controller.myList[29],
|
||||
style:
|
||||
Theme.of(context).textTheme.titleSmall,
|
||||
maxLines: 2, // Allow up to 2 lines
|
||||
overflow: TextOverflow
|
||||
.ellipsis, // Handle overflow
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.flag,
|
||||
color: AppColor.redColor),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
// Keep Expanded here for layout
|
||||
child: Text(
|
||||
controller.myList[30],
|
||||
style:
|
||||
Theme.of(context).textTheme.titleSmall,
|
||||
maxLines: 2, // Allow up to 2 lines
|
||||
overflow: TextOverflow
|
||||
.ellipsis, // Handle overflow
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Card(
|
||||
elevation: 4,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
_InfoTile(
|
||||
icon: Icons.timer,
|
||||
label:
|
||||
'${(double.parse(controller.myList[12]) / 60).toStringAsFixed(0)} ${'min'.tr}',
|
||||
),
|
||||
_InfoTile(
|
||||
icon: Icons.directions_car,
|
||||
label:
|
||||
'${(double.parse(controller.myList[11]) / 1000).toStringAsFixed(1)} ${'km'.tr}',
|
||||
),
|
||||
_InfoTile(
|
||||
icon: Icons.monetization_on,
|
||||
label: '${controller.myList[2]}',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
MyElevatedButton(
|
||||
kolor: AppColor.greenColor,
|
||||
title: 'Accept Order'.tr,
|
||||
onPressed: () async {
|
||||
Get.put(HomeCaptainController()).changeRideId();
|
||||
box.write(BoxName.statusDriverLocation, 'on');
|
||||
controller.endTimer();
|
||||
controller.changeApplied();
|
||||
|
||||
var res = await CRUD().post(
|
||||
link: AppLink.updateStausFromSpeed,
|
||||
payload: {
|
||||
'id': (controller.myList[16]),
|
||||
'rideTimeStart': DateTime.now().toString(),
|
||||
'status': 'Apply',
|
||||
'driver_id': box.read(BoxName.driverID),
|
||||
});
|
||||
if (AppLink.endPoint != AppLink.seferCairoServer) {
|
||||
CRUD().post(
|
||||
link:
|
||||
"${AppLink.endPoint}/ride/rides/updateStausFromSpeed.php",
|
||||
payload: {
|
||||
'id': (controller.myList[16]),
|
||||
'rideTimeStart': DateTime.now().toString(),
|
||||
'status': 'Apply',
|
||||
'driver_id': box.read(BoxName.driverID),
|
||||
});
|
||||
}
|
||||
if (res == 'failure') {
|
||||
MyDialog().getDialog(
|
||||
"This ride is already applied by another driver."
|
||||
.tr,
|
||||
'', () {
|
||||
Get.back();
|
||||
});
|
||||
} else {
|
||||
await CRUD().postFromDialogue(
|
||||
link: AppLink.addDriverOrder,
|
||||
payload: {
|
||||
'driver_id':
|
||||
(controller.myList[6].toString()),
|
||||
'order_id':
|
||||
(controller.myList[16].toString()),
|
||||
'status': 'Apply'
|
||||
});
|
||||
if (AppLink.endPoint !=
|
||||
AppLink.seferCairoServer) {
|
||||
CRUD().postFromDialogue(
|
||||
link:
|
||||
'${AppLink.endPoint}/rides/driver_order/add.php',
|
||||
payload: {
|
||||
'driver_id':
|
||||
(controller.myList[6].toString()),
|
||||
'order_id':
|
||||
(controller.myList[16].toString()),
|
||||
'status': 'Apply'
|
||||
});
|
||||
}
|
||||
List<String> bodyToPassenger = [
|
||||
controller.myList[6].toString(),
|
||||
controller.myList[8].toString(),
|
||||
controller.myList[9].toString(),
|
||||
];
|
||||
FirebaseMessagesController()
|
||||
.sendNotificationToPassengerToken(
|
||||
"Accepted Ride".tr,
|
||||
'your ride is Accepted'.tr,
|
||||
controller.myList[9].toString(),
|
||||
bodyToPassenger,
|
||||
'start.wav');
|
||||
Get.back();
|
||||
box.write(BoxName.rideArguments, {
|
||||
'passengerLocation':
|
||||
controller.myList[0].toString(),
|
||||
'passengerDestination':
|
||||
controller.myList[1].toString(),
|
||||
'Duration': controller.myList[4].toString(),
|
||||
'totalCost': controller.myList[26].toString(),
|
||||
'Distance': controller.myList[5].toString(),
|
||||
'name': controller.myList[8].toString(),
|
||||
'phone': controller.myList[10].toString(),
|
||||
'email': controller.myList[28].toString(),
|
||||
'WalletChecked':
|
||||
controller.myList[13].toString(),
|
||||
'tokenPassenger':
|
||||
controller.myList[9].toString(),
|
||||
'direction':
|
||||
'https://www.google.com/maps/dir/${controller.myList[0]}/${controller.myList[1]}/',
|
||||
'DurationToPassenger':
|
||||
controller.myList[15].toString(),
|
||||
'rideId': (controller.myList[16].toString()),
|
||||
'passengerId':
|
||||
(controller.myList[7].toString()),
|
||||
'driverId': (controller.myList[18].toString()),
|
||||
'durationOfRideValue':
|
||||
controller.myList[19].toString(),
|
||||
'paymentAmount':
|
||||
controller.myList[2].toString(),
|
||||
'paymentMethod':
|
||||
controller.myList[13].toString() == 'true'
|
||||
? 'visa'
|
||||
: 'cash',
|
||||
'isHaveSteps': controller.myList[20].toString(),
|
||||
'step0': controller.myList[21].toString(),
|
||||
'step1': controller.myList[22].toString(),
|
||||
'step2': controller.myList[23].toString(),
|
||||
'step3': controller.myList[24].toString(),
|
||||
'step4': controller.myList[25].toString(),
|
||||
'passengerWalletBurc':
|
||||
controller.myList[26].toString(),
|
||||
'timeOfOrder': DateTime.now().toString(),
|
||||
'totalPassenger':
|
||||
controller.myList[2].toString(),
|
||||
'carType': controller.myList[31].toString(),
|
||||
'kazan': controller.myList[32].toString(),
|
||||
'startNameLocation':
|
||||
controller.myList[29].toString(),
|
||||
'endNameLocation':
|
||||
controller.myList[30].toString(),
|
||||
});
|
||||
Get.to(() => PassengerLocationMapPage(),
|
||||
arguments: box.read(BoxName.rideArguments));
|
||||
}
|
||||
},
|
||||
),
|
||||
GetBuilder<OrderRequestController>(
|
||||
builder: (timerController) {
|
||||
final isNearEnd = timerController.remainingTime <=
|
||||
5; // Define a threshold for "near end"
|
||||
|
||||
return Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
CircularProgressIndicator(
|
||||
value: timerController.progress,
|
||||
// Set the color based on the "isNearEnd" condition
|
||||
color: isNearEnd ? Colors.red : Colors.blue,
|
||||
),
|
||||
Text(
|
||||
'${timerController.remainingTime}',
|
||||
style: AppStyle.number,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
MyElevatedButton(
|
||||
title: 'Refuse Order'.tr,
|
||||
onPressed: () async {
|
||||
controller.endTimer();
|
||||
List<String> bodyToPassenger = [
|
||||
box.read(BoxName.driverID).toString(),
|
||||
box.read(BoxName.nameDriver).toString(),
|
||||
box.read(BoxName.tokenDriver).toString(),
|
||||
];
|
||||
|
||||
FirebaseMessagesController()
|
||||
.sendNotificationToPassengerToken(
|
||||
'Order Under Review'.tr,
|
||||
'${box.read(BoxName.nameDriver)} ${'is reviewing your order. They may need more information or a higher price.'.tr}',
|
||||
controller.myList[9].toString(),
|
||||
bodyToPassenger,
|
||||
'notification.wav');
|
||||
|
||||
controller.refuseOrder(
|
||||
EncryptionHelper.instance.encryptData(
|
||||
controller.myList[16].toString()),
|
||||
);
|
||||
controller.addRideToNotificationDriverString(
|
||||
controller.myList[16].toString(),
|
||||
controller.myList[29].toString(),
|
||||
controller.myList[30].toString(),
|
||||
'${DateTime.now().year}-${DateTime.now().month}-${DateTime.now().day}',
|
||||
'${DateTime.now().hour}:${DateTime.now().minute}',
|
||||
controller.myList[2].toString(),
|
||||
controller.myList[7].toString(),
|
||||
'wait',
|
||||
controller.myList[31].toString(),
|
||||
controller.myList[33].toString(),
|
||||
controller.myList[2].toString(),
|
||||
controller.myList[5].toString(),
|
||||
controller.myList[4].toString());
|
||||
},
|
||||
kolor: AppColor.redColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _InfoTile extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String label;
|
||||
|
||||
const _InfoTile({required this.icon, required this.label});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Icon(icon, color: AppColor.primaryColor),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
label,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
603
lib/views/home/Captin/orderCaptin/order_speed_request.dart
Executable file
603
lib/views/home/Captin/orderCaptin/order_speed_request.dart
Executable file
@@ -0,0 +1,603 @@
|
||||
import 'dart:convert'; // Though not directly used in this version's UI logic, often kept for model interactions.
|
||||
|
||||
import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/constant/box_name.dart';
|
||||
import 'package:sefer_driver/controller/firebase/firbase_messge.dart';
|
||||
import 'package:sefer_driver/main.dart'; // For `box`
|
||||
import 'package:sefer_driver/views/home/Captin/driver_map_page.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import '../../../../constant/colors.dart'; // Your AppColor
|
||||
import '../../../../constant/links.dart'; // Your AppLink
|
||||
import '../../../../constant/style.dart'; // Your AppStyle
|
||||
import '../../../../controller/functions/crud.dart';
|
||||
import '../../../../controller/functions/launch.dart';
|
||||
import '../../../../controller/home/captin/order_request_controller.dart';
|
||||
import '../../../widgets/elevated_btn.dart'; // Your MyElevatedButton
|
||||
|
||||
class OrderSpeedRequest extends StatelessWidget {
|
||||
OrderSpeedRequest({super.key});
|
||||
|
||||
final OrderRequestController orderRequestController =
|
||||
Get.put(OrderRequestController());
|
||||
|
||||
// Helper to make myList access more readable and safer
|
||||
String _getData(int index, {String defaultValue = ''}) {
|
||||
if (orderRequestController.myList.length > index &&
|
||||
orderRequestController.myList[index] != null) {
|
||||
return orderRequestController.myList[index].toString();
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Define AppBar first to get its height for body calculations
|
||||
final appBar = AppBar(
|
||||
title: Text('Speed Order'.tr),
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
onPressed: () => Get.back(),
|
||||
),
|
||||
backgroundColor: AppColor.primaryColor, // Example color, adjust as needed
|
||||
elevation: 2.0,
|
||||
);
|
||||
|
||||
final double appBarHeight = appBar.preferredSize.height;
|
||||
final MediaQueryData mediaQueryData = MediaQuery.of(context);
|
||||
final double screenHeight = mediaQueryData.size.height;
|
||||
final double statusBarHeight = mediaQueryData.padding.top;
|
||||
final double bottomSystemPadding = mediaQueryData.padding.bottom;
|
||||
|
||||
// Calculate available height for the Scaffold's body content
|
||||
// Subtracting status bar, app bar, and bottom system padding (like navigation bar)
|
||||
final double availableBodyHeight =
|
||||
screenHeight - appBarHeight - statusBarHeight - bottomSystemPadding;
|
||||
|
||||
// Define overall padding for the body content
|
||||
const EdgeInsets bodyContentPadding =
|
||||
EdgeInsets.symmetric(horizontal: 10.0, vertical: 8.0);
|
||||
|
||||
// Calculate the height for the main content Column, considering the body's own padding
|
||||
double mainColumnHeight = availableBodyHeight - bodyContentPadding.vertical;
|
||||
if (mainColumnHeight < 0) mainColumnHeight = 0;
|
||||
|
||||
return GetBuilder<OrderRequestController>(
|
||||
builder: (controller) {
|
||||
// Pre-extract data for readability and safety
|
||||
final String price =
|
||||
double.tryParse(_getData(2))?.toStringAsFixed(2) ?? 'N/A';
|
||||
final bool isComfortTrip = _getData(31) == 'Comfort';
|
||||
final String carType = _getData(31).tr;
|
||||
|
||||
final String pickupName = _getData(12);
|
||||
final String pickupDetails = '(${_getData(11)})';
|
||||
final String pickupFullAddress = _getData(29);
|
||||
|
||||
final String dropoffName = _getData(5);
|
||||
final String dropoffDetails = '(${_getData(4)})';
|
||||
final String dropoffFullAddress = _getData(30);
|
||||
|
||||
final String passengerName = _getData(8);
|
||||
final String passengerRating = _getData(33);
|
||||
|
||||
final bool isVisaPayment = _getData(13) == 'true';
|
||||
final bool hasSteps = _getData(20) == 'haveSteps';
|
||||
|
||||
final String mapUrl =
|
||||
'https://www.google.com/maps/dir/${_getData(0)}/${_getData(1)}/';
|
||||
final String rideId = _getData(16); // Commonly used ID
|
||||
|
||||
return Scaffold(
|
||||
appBar: appBar,
|
||||
backgroundColor: AppColor.secondaryColor ??
|
||||
Colors.grey[100], // Background for the page
|
||||
body: SafeArea(
|
||||
// Ensures content is not obscured by system UI
|
||||
child: Padding(
|
||||
padding:
|
||||
bodyContentPadding, // Apply overall padding to the body's content area
|
||||
child: SizedBox(
|
||||
// Constrain the height of the main layout Column
|
||||
height: mainColumnHeight,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// --- MAP SECTION ---
|
||||
SizedBox(
|
||||
height:
|
||||
Get.height * 0.28, // Relative to total screen height
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
child: GoogleMap(
|
||||
initialCameraPosition: CameraPosition(
|
||||
zoom: 12,
|
||||
target:
|
||||
Get.find<HomeCaptainController>().myLocation),
|
||||
cameraTargetBounds:
|
||||
CameraTargetBounds(controller.bounds),
|
||||
myLocationButtonEnabled: false,
|
||||
trafficEnabled: false,
|
||||
buildingsEnabled: false,
|
||||
mapToolbarEnabled: false,
|
||||
myLocationEnabled: true,
|
||||
markers: {
|
||||
Marker(
|
||||
markerId: MarkerId('MyLocation'.tr),
|
||||
position: LatLng(
|
||||
controller.latPassengerLocation,
|
||||
controller.lngPassengerLocation),
|
||||
icon: controller.startIcon),
|
||||
Marker(
|
||||
markerId: MarkerId('Destination'.tr),
|
||||
position: LatLng(
|
||||
controller.latPassengerDestination,
|
||||
controller.lngPassengerDestination),
|
||||
icon: controller.endIcon),
|
||||
},
|
||||
polylines: {
|
||||
Polyline(
|
||||
zIndex: 1,
|
||||
consumeTapEvents: true,
|
||||
geodesic: true,
|
||||
endCap: Cap.buttCap,
|
||||
startCap: Cap.buttCap,
|
||||
visible: true,
|
||||
polylineId: const PolylineId('routeOrder'),
|
||||
points: controller.pointsDirection,
|
||||
color: AppColor.primaryColor,
|
||||
width: 3,
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// --- PRICE & TRIP TYPE SECTION ---
|
||||
Card(
|
||||
elevation: 3,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 12.0, horizontal: 16.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
price,
|
||||
style: AppStyle.headTitle.copyWith(
|
||||
color: AppColor.primaryColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 28),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
carType,
|
||||
style: AppStyle.title.copyWith(
|
||||
color: AppColor.greenColor,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
if (isComfortTrip)
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.ac_unit,
|
||||
color: AppColor.blueColor, size: 18),
|
||||
const SizedBox(width: 4),
|
||||
Text('Air condition Trip'.tr,
|
||||
style: AppStyle.subtitle
|
||||
.copyWith(fontSize: 13)),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// --- EXPANDED SECTION FOR SCROLLABLE (BUT NOT USER-SCROLLABLE) CONTENT ---
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
physics:
|
||||
const NeverScrollableScrollPhysics(), // Prevents user scrolling
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize
|
||||
.min, // Takes minimum vertical space needed
|
||||
children: [
|
||||
_buildLocationCard(
|
||||
icon: Icons.arrow_circle_up,
|
||||
iconColor: AppColor.greenColor,
|
||||
title: pickupName,
|
||||
subtitle: pickupDetails,
|
||||
fullAddress: pickupFullAddress,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildLocationCard(
|
||||
icon: Icons.arrow_circle_down,
|
||||
iconColor: AppColor.redColor,
|
||||
title: dropoffName,
|
||||
subtitle: dropoffDetails,
|
||||
fullAddress: dropoffFullAddress,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
// --- PAYMENT, STEPS & DIRECTIONS INFO ---
|
||||
Card(
|
||||
elevation: 2,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
isVisaPayment
|
||||
? Icons.credit_card
|
||||
: Icons
|
||||
.payments_outlined, // Using payments_outlined for cash
|
||||
color: isVisaPayment
|
||||
? AppColor.deepPurpleAccent
|
||||
: AppColor.greenColor,
|
||||
size: 24,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
isVisaPayment ? 'Visa'.tr : 'Cash'.tr,
|
||||
style: AppStyle.title.copyWith(
|
||||
fontWeight: FontWeight.w600),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (hasSteps)
|
||||
Expanded(
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons
|
||||
.format_list_numbered_rtl_outlined,
|
||||
color: AppColor.bronze,
|
||||
size: 24),
|
||||
const SizedBox(width: 4),
|
||||
Flexible(
|
||||
child: Text(
|
||||
'Trip has Steps'.tr,
|
||||
style: AppStyle.title.copyWith(
|
||||
color: AppColor.bronze,
|
||||
fontSize: 13),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
tapTargetSize:
|
||||
MaterialTapTargetSize.shrinkWrap,
|
||||
alignment: Alignment.centerRight,
|
||||
),
|
||||
onPressed: () => showInBrowser(mapUrl),
|
||||
icon: const Icon(
|
||||
Icons.directions_outlined,
|
||||
color: AppColor.blueColor,
|
||||
size: 20),
|
||||
label: Text("Directions".tr,
|
||||
style: AppStyle.subtitle.copyWith(
|
||||
color: AppColor.blueColor,
|
||||
fontSize: 13)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
// --- PASSENGER INFO ---
|
||||
Card(
|
||||
elevation: 2,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0, vertical: 10.0),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.person_outline,
|
||||
color: AppColor.greyColor, size: 22),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Text(
|
||||
passengerName,
|
||||
style: AppStyle.title,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
const Icon(Icons.star_rounded,
|
||||
color: Colors.amber, size: 20),
|
||||
Text(
|
||||
passengerRating,
|
||||
style: AppStyle.title.copyWith(
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// const SizedBox(height: 8), // Spacer before action buttons if needed
|
||||
|
||||
// --- ACTION BUTTONS & TIMER ---
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0, bottom: 5.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: MyElevatedButton(
|
||||
kolor: AppColor.greenColor,
|
||||
title: 'Accept Order'.tr,
|
||||
onPressed: () async {
|
||||
Get.put(HomeCaptainController()).changeRideId();
|
||||
box.write(BoxName.statusDriverLocation, 'on');
|
||||
var res = await CRUD().post(
|
||||
link: AppLink.updateStausFromSpeed,
|
||||
payload: {
|
||||
'id': rideId,
|
||||
'rideTimeStart':
|
||||
DateTime.now().toString(),
|
||||
'status': 'Apply',
|
||||
'driver_id': box.read(BoxName.driverID),
|
||||
});
|
||||
if (AppLink.endPoint !=
|
||||
AppLink.seferCairoServer) {
|
||||
CRUD().post(
|
||||
link:
|
||||
"${AppLink.endPoint}/ride/rides/updateStausFromSpeed.php",
|
||||
payload: {
|
||||
'id': rideId,
|
||||
'rideTimeStart':
|
||||
DateTime.now().toString(),
|
||||
'status': 'Apply',
|
||||
'driver_id': box.read(BoxName.driverID),
|
||||
});
|
||||
}
|
||||
if (res != "failure") {
|
||||
box.write(BoxName.statusDriverLocation, 'on');
|
||||
controller.changeApplied();
|
||||
List<String> bodyToPassenger = [
|
||||
box.read(BoxName.driverID).toString(),
|
||||
box.read(BoxName.nameDriver).toString(),
|
||||
box.read(BoxName.tokenDriver).toString(),
|
||||
rideId.toString(),
|
||||
];
|
||||
Get.put(FirebaseMessagesController())
|
||||
.sendNotificationToDriverMAP(
|
||||
'Accepted Ride',
|
||||
'your ride is applied'.tr,
|
||||
controller.arguments?['DriverList']
|
||||
?[9]
|
||||
?.toString() ??
|
||||
_getData(9), // Safer access
|
||||
bodyToPassenger,
|
||||
'start.wav');
|
||||
|
||||
// Using rideId (_getData(16)) for order_id consistently
|
||||
CRUD().postFromDialogue(
|
||||
link: AppLink.addDriverOrder,
|
||||
payload: {
|
||||
'driver_id': _getData(
|
||||
6), // Driver ID from the order data
|
||||
'order_id': rideId,
|
||||
'status': 'Apply'
|
||||
});
|
||||
|
||||
if (AppLink.endPoint !=
|
||||
AppLink.seferCairoServer) {
|
||||
CRUD().post(
|
||||
link:
|
||||
"${AppLink.endPoint}/ride/driver_order/add.php",
|
||||
payload: {
|
||||
'driver_id': _getData(6),
|
||||
'order_id': rideId,
|
||||
'status': 'Apply'
|
||||
});
|
||||
}
|
||||
|
||||
Get.back(); // Go back from order request screen
|
||||
box.write(BoxName.rideArguments, {
|
||||
'passengerLocation': _getData(0),
|
||||
'passengerDestination': _getData(1),
|
||||
'Duration': _getData(4),
|
||||
'totalCost': _getData(26),
|
||||
'Distance': _getData(5),
|
||||
'name': _getData(8),
|
||||
'phone': _getData(10),
|
||||
'email': _getData(28),
|
||||
'WalletChecked': _getData(13),
|
||||
'tokenPassenger': _getData(9),
|
||||
'direction': mapUrl,
|
||||
'DurationToPassenger': _getData(15),
|
||||
'rideId': rideId,
|
||||
'passengerId': _getData(7),
|
||||
'driverId': box
|
||||
.read(BoxName.driverID)
|
||||
.toString(), // Current driver accepting
|
||||
'durationOfRideValue': _getData(19),
|
||||
'paymentAmount': _getData(2),
|
||||
'paymentMethod': _getData(13) == 'true'
|
||||
? 'visa'
|
||||
: 'cash',
|
||||
'isHaveSteps': _getData(20),
|
||||
'step0': _getData(21),
|
||||
'step1': _getData(22),
|
||||
'step2': _getData(23),
|
||||
'step3': _getData(24),
|
||||
'step4': _getData(25),
|
||||
'passengerWalletBurc': _getData(26),
|
||||
'timeOfOrder': DateTime.now().toString(),
|
||||
'totalPassenger': _getData(
|
||||
2), // This is likely trip cost for passenger
|
||||
'carType': _getData(31),
|
||||
'kazan':
|
||||
_getData(32), // Driver's commission/cut
|
||||
'startNameLocation': _getData(29),
|
||||
'endNameLocation': _getData(30),
|
||||
});
|
||||
Get.to(() => PassengerLocationMapPage(),
|
||||
arguments:
|
||||
box.read(BoxName.rideArguments));
|
||||
} else {
|
||||
Get.defaultDialog(
|
||||
title:
|
||||
"This ride is already taken by another driver."
|
||||
.tr,
|
||||
middleText: '',
|
||||
titleStyle: AppStyle.title,
|
||||
middleTextStyle: AppStyle.title,
|
||||
confirm: MyElevatedButton(
|
||||
title: 'Ok'.tr,
|
||||
onPressed: () {
|
||||
Get.back(); // Close dialog
|
||||
Get.back(); // Close order request screen
|
||||
}));
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
// --- TIMER ---
|
||||
GetBuilder<OrderRequestController>(
|
||||
id: 'timerUpdate', // Ensure controller calls update(['timerUpdate']) for this
|
||||
builder: (timerCtrl) {
|
||||
final isNearEnd =
|
||||
timerCtrl.remainingTimeSpeed <= 5;
|
||||
return SizedBox(
|
||||
width: 60,
|
||||
height: 60,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
CircularProgressIndicator(
|
||||
value: timerCtrl.progressSpeed,
|
||||
color: isNearEnd
|
||||
? Colors.redAccent
|
||||
: AppColor.primaryColor,
|
||||
strokeWidth: 5,
|
||||
backgroundColor: Colors.grey.shade300,
|
||||
),
|
||||
Text('${timerCtrl.remainingTimeSpeed}',
|
||||
style: AppStyle.headTitle2.copyWith(
|
||||
color: isNearEnd
|
||||
? Colors.redAccent
|
||||
: AppColor.writeColor ??
|
||||
Colors.black,
|
||||
)),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: MyElevatedButton(
|
||||
title: 'Refuse Order'.tr,
|
||||
onPressed: () async {
|
||||
controller.endTimer();
|
||||
controller.refuseOrder(rideId);
|
||||
controller.addRideToNotificationDriverString(
|
||||
rideId,
|
||||
_getData(29),
|
||||
_getData(30),
|
||||
'${DateTime.now().year}-${DateTime.now().month}-${DateTime.now().day}',
|
||||
'${DateTime.now().hour}:${DateTime.now().minute}',
|
||||
_getData(2),
|
||||
_getData(7),
|
||||
'wait',
|
||||
_getData(31),
|
||||
_getData(33),
|
||||
_getData(2),
|
||||
_getData(5),
|
||||
_getData(4));
|
||||
// Get.back(); // refuseOrder or endTimer should handle navigation if needed.
|
||||
// If not, add Get.back(); here.
|
||||
},
|
||||
kolor: AppColor.redColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Helper widget for location cards to reduce repetition and improve readability
|
||||
Widget _buildLocationCard(
|
||||
{required IconData icon,
|
||||
required Color iconColor,
|
||||
required String title,
|
||||
required String subtitle,
|
||||
required String fullAddress}) {
|
||||
return Card(
|
||||
elevation: 2,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
margin: const EdgeInsets.symmetric(
|
||||
vertical: 4), // Add a little vertical margin between cards
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Icon(icon, color: iconColor, size: 28),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"$title $subtitle"
|
||||
.trim(), // Trim to avoid extra spaces if subtitle is empty
|
||||
style: AppStyle.title.copyWith(fontWeight: FontWeight.w600),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
if (fullAddress.isNotEmpty) ...[
|
||||
const SizedBox(height: 3),
|
||||
Text(
|
||||
fullAddress,
|
||||
style: AppStyle.subtitle
|
||||
.copyWith(fontSize: 13, color: AppColor.greyColor),
|
||||
maxLines: 2, // Allow up to 2 lines for address
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
230
lib/views/home/Captin/orderCaptin/test_order_page.dart
Executable file
230
lib/views/home/Captin/orderCaptin/test_order_page.dart
Executable file
@@ -0,0 +1,230 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import '../../../../constant/colors.dart';
|
||||
import '../../../../controller/home/captin/home_captain_controller.dart';
|
||||
|
||||
class OrderRequestPageTest extends StatefulWidget {
|
||||
const OrderRequestPageTest({super.key});
|
||||
|
||||
@override
|
||||
State<OrderRequestPageTest> createState() => _OrderRequestPageTestState();
|
||||
}
|
||||
|
||||
class _OrderRequestPageTestState extends State<OrderRequestPageTest> {
|
||||
late OrderRequestController _orderRequestController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Initialize the controller and process arguments
|
||||
_initializeController();
|
||||
}
|
||||
|
||||
void _initializeController() {
|
||||
// Get the controller or create a new one if not exists
|
||||
_orderRequestController = Get.put(OrderRequestController());
|
||||
|
||||
// Process arguments passed to the page
|
||||
final arguments = Get.arguments;
|
||||
final myListString = arguments['myListString'];
|
||||
var myList =
|
||||
arguments['DriverList'] == null || arguments['DriverList'].isEmpty
|
||||
? jsonDecode(myListString)
|
||||
: arguments['DriverList'];
|
||||
|
||||
// Parse coordinates and prepare map data
|
||||
_orderRequestController.parseCoordinates(myList);
|
||||
|
||||
// Start timer and calculate fuel consumption
|
||||
_orderRequestController.startTimer(
|
||||
myList[6].toString(),
|
||||
myList[16].toString(),
|
||||
);
|
||||
_orderRequestController.calculateConsumptionFuel();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6),
|
||||
child: Container(
|
||||
color: const Color.fromARGB(255, 241, 238, 238),
|
||||
child: ListView(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: Get.height * .33,
|
||||
child: Obx(() => GoogleMap(
|
||||
initialCameraPosition: CameraPosition(
|
||||
zoom: 12,
|
||||
target:
|
||||
Get.find<HomeCaptainController>().myLocation,
|
||||
),
|
||||
cameraTargetBounds: CameraTargetBounds(
|
||||
_orderRequestController.mapBounds.value),
|
||||
myLocationButtonEnabled: true,
|
||||
trafficEnabled: false,
|
||||
buildingsEnabled: false,
|
||||
mapToolbarEnabled: true,
|
||||
myLocationEnabled: true,
|
||||
markers: _orderRequestController.markers.value,
|
||||
polylines: _orderRequestController.polylines.value,
|
||||
onMapCreated: (GoogleMapController controller) {
|
||||
_orderRequestController.mapController.value =
|
||||
controller;
|
||||
},
|
||||
onCameraMove: (CameraPosition position) {
|
||||
_orderRequestController
|
||||
.updateCameraPosition(position);
|
||||
},
|
||||
)),
|
||||
),
|
||||
// Rest of your UI components
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OrderRequestController extends GetxController {
|
||||
// Reactive variables for map-related data
|
||||
Rx<LatLngBounds> mapBounds = Rx<LatLngBounds>(LatLngBounds(
|
||||
southwest: const LatLng(0, 0), northeast: const LatLng(0, 0)));
|
||||
|
||||
Rx<Set<Marker>> markers = Rx<Set<Marker>>({});
|
||||
Rx<Set<Polyline>> polylines = Rx<Set<Polyline>>({});
|
||||
|
||||
Rx<GoogleMapController?> mapController = Rx<GoogleMapController?>(null);
|
||||
|
||||
// Icons for start and end markers
|
||||
late BitmapDescriptor startIcon;
|
||||
late BitmapDescriptor endIcon;
|
||||
|
||||
// Coordinates for passenger location and destination
|
||||
Rx<LatLng?> passengerLocation = Rx<LatLng?>(null);
|
||||
Rx<LatLng?> passengerDestination = Rx<LatLng?>(null);
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
// Initialize marker icons
|
||||
_initializeMarkerIcons();
|
||||
}
|
||||
|
||||
void _initializeMarkerIcons() async {
|
||||
// Load custom marker icons
|
||||
startIcon = await BitmapDescriptor.fromAssetImage(
|
||||
const ImageConfiguration(size: Size(48, 48)),
|
||||
'assets/start_marker.png');
|
||||
|
||||
endIcon = await BitmapDescriptor.fromAssetImage(
|
||||
const ImageConfiguration(size: Size(48, 48)), 'assets/end_marker.png');
|
||||
}
|
||||
|
||||
void parseCoordinates(List myList) {
|
||||
// Parse coordinates from the input list
|
||||
var cords = myList[0].split(',');
|
||||
var cordDestination = myList[1].split(',');
|
||||
|
||||
double latPassengerLocation = double.parse(cords[0]);
|
||||
double lngPassengerLocation = double.parse(cords[1]);
|
||||
double latPassengerDestination = double.parse(cordDestination[0]);
|
||||
double lngPassengerDestination = double.parse(cordDestination[1]);
|
||||
|
||||
// Update passenger location and destination
|
||||
passengerLocation.value =
|
||||
LatLng(latPassengerLocation, lngPassengerLocation);
|
||||
passengerDestination.value =
|
||||
LatLng(latPassengerDestination, lngPassengerDestination);
|
||||
|
||||
// Create markers
|
||||
_createMarkers();
|
||||
|
||||
// Create polyline
|
||||
_createPolyline();
|
||||
|
||||
// Calculate map bounds
|
||||
_calculateMapBounds();
|
||||
}
|
||||
|
||||
void _createMarkers() {
|
||||
if (passengerLocation.value == null || passengerDestination.value == null)
|
||||
return;
|
||||
|
||||
markers.value = {
|
||||
Marker(
|
||||
markerId: MarkerId('MyLocation'.tr),
|
||||
position: passengerLocation.value!,
|
||||
draggable: true,
|
||||
icon: startIcon,
|
||||
),
|
||||
Marker(
|
||||
markerId: MarkerId('Destination'.tr),
|
||||
position: passengerDestination.value!,
|
||||
draggable: true,
|
||||
icon: endIcon,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
void _createPolyline() {
|
||||
if (passengerLocation.value == null || passengerDestination.value == null)
|
||||
return;
|
||||
|
||||
polylines.value = {
|
||||
Polyline(
|
||||
zIndex: 1,
|
||||
consumeTapEvents: true,
|
||||
geodesic: true,
|
||||
endCap: Cap.buttCap,
|
||||
startCap: Cap.buttCap,
|
||||
visible: true,
|
||||
polylineId: const PolylineId('routeOrder'),
|
||||
points: [passengerLocation.value!, passengerDestination.value!],
|
||||
color: AppColor.primaryColor,
|
||||
width: 2,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
void _calculateMapBounds() {
|
||||
if (passengerLocation.value == null || passengerDestination.value == null)
|
||||
return;
|
||||
|
||||
double minLatitude = math.min(passengerLocation.value!.latitude,
|
||||
passengerDestination.value!.latitude);
|
||||
double maxLatitude = math.max(passengerLocation.value!.latitude,
|
||||
passengerDestination.value!.latitude);
|
||||
double minLongitude = math.min(passengerLocation.value!.longitude,
|
||||
passengerDestination.value!.longitude);
|
||||
double maxLongitude = math.max(passengerLocation.value!.longitude,
|
||||
passengerDestination.value!.longitude);
|
||||
|
||||
mapBounds.value = LatLngBounds(
|
||||
southwest: LatLng(minLatitude, minLongitude),
|
||||
northeast: LatLng(maxLatitude, maxLongitude),
|
||||
);
|
||||
}
|
||||
|
||||
void updateCameraPosition(CameraPosition position) {
|
||||
// Implement any specific logic for camera position updates
|
||||
}
|
||||
|
||||
void startTimer(String param1, String param2) {
|
||||
// Implement timer start logic
|
||||
}
|
||||
|
||||
void calculateConsumptionFuel() {
|
||||
// Implement fuel consumption calculation
|
||||
}
|
||||
}
|
||||
237
lib/views/home/Captin/orderCaptin/vip_order_page.dart
Executable file
237
lib/views/home/Captin/orderCaptin/vip_order_page.dart
Executable file
@@ -0,0 +1,237 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:sefer_driver/constant/box_name.dart';
|
||||
import 'package:sefer_driver/constant/links.dart';
|
||||
import 'package:sefer_driver/controller/functions/crud.dart';
|
||||
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../../main.dart';
|
||||
import '../../../../print.dart';
|
||||
|
||||
class VipOrderPage extends StatelessWidget {
|
||||
const VipOrderPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(VipOrderController());
|
||||
return MyScafolld(
|
||||
title: 'VIP Order'.tr,
|
||||
body: [
|
||||
GetBuilder<VipOrderController>(builder: (vipOrderController) {
|
||||
if (vipOrderController.isLoading) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
|
||||
if (vipOrderController.tripData.isEmpty) {
|
||||
return Center(
|
||||
child: Text('No orders available'.tr),
|
||||
);
|
||||
}
|
||||
final order = vipOrderController.tripData[0];
|
||||
Log.print('order: ${order}');
|
||||
return SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Passenger Section
|
||||
_buildSectionTitle('Passenger Information'.tr),
|
||||
_buildInfoCard(
|
||||
children: [
|
||||
_buildDetailRow('Name'.tr,
|
||||
'${order['passengerName'] ?? 'Unknown'} ${order['passengerLastName'] ?? ''}'),
|
||||
_buildDetailRow(
|
||||
'Phone'.tr, order['passengerPhone'] ?? 'Unknown'),
|
||||
_buildDetailRow(
|
||||
'Gender'.tr, order['passengergender'] ?? 'Unknown'),
|
||||
_buildDetailRow('time Selected'.tr,
|
||||
order['timeSelected'] ?? 'Unknown'),
|
||||
_buildDetailRow(
|
||||
'Ride Status'.tr, order['status'] ?? 'Unknown'),
|
||||
// _buildDetailRow('Ride Status'.tr,
|
||||
// vipOrderController.myList[4] ?? 'Unknown'),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
// print(vipOrderController.myList);
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Action Buttons
|
||||
_buildActionButtons(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
})
|
||||
],
|
||||
isleading: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildSectionTitle(String title) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInfoCard({required List<Widget> children}) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.2),
|
||||
spreadRadius: 1,
|
||||
blurRadius: 5,
|
||||
offset: const Offset(0, 3),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: children,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDetailRow(String label, String value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Text(
|
||||
value,
|
||||
style: const TextStyle(color: Colors.black54),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionButtons(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () => _onReject(context),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.red,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
'Reject'.tr,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () => _onApply(context),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.green,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
'Apply'.tr,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _onReject(BuildContext context) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Ride Rejected'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onApply(BuildContext context) {
|
||||
// TODO: Implement application logic
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Ride Applied'),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class VipOrderController extends GetxController {
|
||||
bool isLoading = false;
|
||||
List tripData = [];
|
||||
|
||||
fetchOrder() async {
|
||||
isLoading = true; // Set loading state
|
||||
update(); // Notify listeners
|
||||
var res = await CRUD().get(link: AppLink.getMishwariDriver, payload: {
|
||||
'driverId': box.read(BoxName.driverID).toString(),
|
||||
});
|
||||
if (res != 'failure') {
|
||||
tripData = jsonDecode(res)['message'];
|
||||
} else {
|
||||
tripData = [];
|
||||
}
|
||||
isLoading = false; // Loading complete
|
||||
update(); // Notify listeners
|
||||
}
|
||||
|
||||
@override
|
||||
void onInit() async {
|
||||
fetchOrder();
|
||||
super.onInit();
|
||||
}
|
||||
}
|
||||
46
lib/views/home/Captin/passportimage.dart
Executable file
46
lib/views/home/Captin/passportimage.dart
Executable file
@@ -0,0 +1,46 @@
|
||||
// import 'dart:io';
|
||||
//
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:get/get.dart';
|
||||
// import '../../../controller/functions/ocr_controller.dart';
|
||||
//
|
||||
// class PassportDataExtractorWidget extends StatelessWidget {
|
||||
// final PassportDataExtractor passportDataExtractor =
|
||||
// Get.put(PassportDataExtractor());
|
||||
// final PassportDataController controller = Get.put(PassportDataController());
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Scaffold(
|
||||
// appBar: AppBar(
|
||||
// title: const Text('Passport Data Extractor'),
|
||||
// ),
|
||||
// body: Column(
|
||||
// children: [
|
||||
// ElevatedButton(
|
||||
// onPressed: controller.extractDataAndDrawBoundingBoxes,
|
||||
// child: const Text('Extract Data'),
|
||||
// ),
|
||||
// Expanded(
|
||||
// child: Center(
|
||||
// child: Stack(
|
||||
// children: [
|
||||
// GetBuilder<PassportDataController>(
|
||||
// builder: (controller) => CustomPaint(
|
||||
// painter: BoundingBoxPainter(
|
||||
// controller.extractedTextWithCoordinates),
|
||||
// child: GetBuilder<PassportDataExtractor>(
|
||||
// builder: (controller) =>
|
||||
// Image.file(File(passportDataExtractor.image!.path)),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
175
lib/views/home/Captin/text_scanner.dart
Executable file
175
lib/views/home/Captin/text_scanner.dart
Executable file
@@ -0,0 +1,175 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/views/widgets/mycircular.dart';
|
||||
|
||||
import '../../../controller/functions/ocr_controller.dart';
|
||||
|
||||
class TextRecognizerAPI extends StatelessWidget {
|
||||
const TextRecognizerAPI({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(ScanDocumentsByApi());
|
||||
return GetBuilder<ScanDocumentsByApi>(
|
||||
builder: (controller) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Api'),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
controller.matchFaceApi();
|
||||
},
|
||||
icon: const Icon(Icons.face),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Center(
|
||||
child: controller.isLoading
|
||||
? const MyCircularProgressIndicator()
|
||||
: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Image.memory(
|
||||
controller.imagePortrait,
|
||||
width: 60,
|
||||
),
|
||||
Image.memory(
|
||||
controller.imageSignature,
|
||||
width: 60,
|
||||
),
|
||||
Image.memory(
|
||||
controller.imageDocumentFrontSide,
|
||||
width: 250,
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(controller.responseMap['authenticity_meta']
|
||||
.toString()),
|
||||
Text(
|
||||
'countryName: ${controller.responseMap['data']['countryName'].toString()}'),
|
||||
Text(
|
||||
'Expiry Date: ${controller.responseMap['data']['ocr']['dateOfExpiry'].toString()}'),
|
||||
// Add more Text widgets to display other record values
|
||||
Text(
|
||||
'Address: ${controller.responseMap['data']['ocr']['address'].toString()}'),
|
||||
Text(
|
||||
'City: ${controller.responseMap['data']['ocr']['addressCity'].toString()}'),
|
||||
Text(
|
||||
'Jurisdiction Code: ${controller.responseMap['data']['ocr']['addressJurisdictionCode'].toString()}'),
|
||||
Text(
|
||||
'Postal Code: ${controller.responseMap['data']['ocr']['addressPostalCode'].toString()}'),
|
||||
Text(
|
||||
'Street: ${controller.responseMap['data']['ocr']['addressStreet'].toString()}'),
|
||||
Text(
|
||||
'Date of Birth: ${controller.responseMap['data']['ocr']['dateOfBirth'].toString()}'),
|
||||
Text(
|
||||
'Date of Issue: ${controller.responseMap['data']['ocr']['dateOfIssue'].toString()}'),
|
||||
Text(
|
||||
'Drivers License Class: ${controller.responseMap['data']['ocr']['dlClass'].toString()}'),
|
||||
Text(
|
||||
'Document Number: ${controller.responseMap['data']['ocr']['documentNumber'].toString()}'),
|
||||
Text(
|
||||
'Eye Color: ${controller.responseMap['data']['ocr']['eyesColor'].toString()}'),
|
||||
Text(
|
||||
'Given Names: ${controller.responseMap['data']['ocr']['givenNames'].toString()}'),
|
||||
Text(
|
||||
'Height: ${controller.responseMap['data']['ocr']['height'].toString()}'),
|
||||
Text(
|
||||
'Issuing State Code: ${controller.responseMap['data']['ocr']['issuingStateCode'].toString()}'),
|
||||
Text(
|
||||
'Name: ${controller.responseMap['data']['ocr']['name'].toString()}'),
|
||||
Text(
|
||||
'Sex: ${controller.responseMap['data']['ocr']['sex'].toString()}'),
|
||||
Text(
|
||||
'Surname: ${controller.responseMap['data']['ocr']['surname'].toString()}'),
|
||||
Text(
|
||||
'Valid State: ${controller.responseMap['data']['ocr']['validState'].toString()}'),
|
||||
],
|
||||
),
|
||||
))));
|
||||
}
|
||||
}
|
||||
// class TextExtractionView extends StatelessWidget {
|
||||
// TextExtractionView({super.key});
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// Get.put(TextExtractionController());
|
||||
// return Scaffold(
|
||||
// appBar: AppBar(
|
||||
// title: const Text('Text Extraction'),
|
||||
// ),
|
||||
// body: GetBuilder<TextExtractionController>(builder: (controller) {
|
||||
// return Center(
|
||||
// child: Column(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// ElevatedButton(
|
||||
// onPressed: () {},
|
||||
// child: const Text('Pick Image and Extract Text'),
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
// controller.isloading
|
||||
// ? const MyCircularProgressIndicator()
|
||||
// : Text(controller.extractedText),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
// class TextRecognizerWidget extends StatelessWidget {
|
||||
// const TextRecognizerWidget({super.key});
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// Get.put(TextMLGoogleRecognizerController());
|
||||
// return GetBuilder<TextMLGoogleRecognizerController>(
|
||||
// builder: (controller) => Scaffold(
|
||||
// appBar: AppBar(),
|
||||
// body: Center(
|
||||
// child: Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// Text('${controller.decode['DRIVER_LICENSE'].toString()}'),
|
||||
// Text('DL: ${controller.decode['dl_number'].toString()}'),
|
||||
// Text(
|
||||
// 'Expiry Date: ${controller.decode['expiry_date'].toString()}'),
|
||||
// Text('Last Name: ${controller.decode['lastName'].toString()}'),
|
||||
// Text(
|
||||
// 'First Name: ${controller.decode['firstName'].toString()}'),
|
||||
// Text('Address: ${controller.decode['address'].toString()}'),
|
||||
// Text('Date of Birth: ${controller.decode['dob'].toString()}'),
|
||||
// Text('RSTR: ${controller.decode['rstr'].toString()}'),
|
||||
// Text('Class: ${controller.decode['class'].toString()}'),
|
||||
// Text('End: ${controller.decode['end'].toString()}'),
|
||||
// Text('DD: ${controller.decode['dd'].toString()}'),
|
||||
// Text('Sex: ${controller.decode['sex'].toString()}'),
|
||||
// Text('Hair: ${controller.decode['hair'].toString()}'),
|
||||
// Text('Eyes: ${controller.decode['eyes'].toString()}'),
|
||||
// // and so on for other fields
|
||||
// ],
|
||||
// ))));
|
||||
// }
|
||||
// }
|
||||
|
||||
// class PassportPage extends StatelessWidget {
|
||||
// PassportPage({super.key});
|
||||
// PassportRecognizerController passportRecognizerController =
|
||||
// Get.put(PassportRecognizerController());
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Scaffold(
|
||||
// appBar: AppBar(
|
||||
// title: const Text('Driver License'),
|
||||
// ),
|
||||
// body: const Center(child: Text('data')));
|
||||
// }
|
||||
// }
|
||||
140
lib/views/home/my_wallet/bank_account_egypt.dart
Executable file
140
lib/views/home/my_wallet/bank_account_egypt.dart
Executable file
@@ -0,0 +1,140 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class BankController extends GetxController {
|
||||
String selectedBank = '';
|
||||
|
||||
Map<String, String> bankNames = {
|
||||
'Ahli United Bank'.tr: 'AUB',
|
||||
'Citi Bank N.A. Egypt'.tr: 'CITI',
|
||||
'MIDBANK'.tr: 'MIDB',
|
||||
'Banque Du Caire'.tr: 'BDC',
|
||||
'HSBC Bank Egypt S.A.E'.tr: 'HSBC',
|
||||
'Credit Agricole Egypt S.A.E'.tr: 'ECAE',
|
||||
'Egyptian Gulf Bank'.tr: 'EGB',
|
||||
'The United Bank'.tr: 'UB',
|
||||
'Qatar National Bank Alahli'.tr: 'QNB',
|
||||
'Arab Bank PLC'.tr: 'ARAB',
|
||||
'Emirates National Bank of Dubai'.tr: 'ENBD',
|
||||
'Al Ahli Bank of Kuwait – Egypt'.tr: 'ABK',
|
||||
'National Bank of Kuwait – Egypt'.tr: 'NBK',
|
||||
'Arab Banking Corporation - Egypt S.A.E'.tr: 'EABC',
|
||||
'First Abu Dhabi Bank'.tr: 'FAB',
|
||||
'Abu Dhabi Islamic Bank – Egypt'.tr: 'ADIB',
|
||||
'Commercial International Bank - Egypt S.A.E'.tr: 'CIB',
|
||||
'Housing And Development Bank'.tr: 'HDB',
|
||||
'Banque Misr'.tr: 'MISR',
|
||||
'Arab African International Bank'.tr: 'AAIB',
|
||||
'Egyptian Arab Land Bank'.tr: 'EALB',
|
||||
'Export Development Bank of Egypt'.tr: 'EDBE',
|
||||
'Faisal Islamic Bank of Egypt'.tr: 'FAIB',
|
||||
'Blom Bank'.tr: 'BLOM',
|
||||
'Abu Dhabi Commercial Bank – Egypt'.tr: 'ADCB',
|
||||
'Alex Bank Egypt'.tr: 'BOA',
|
||||
'Societe Arabe Internationale De Banque'.tr: 'SAIB',
|
||||
'National Bank of Egypt'.tr: 'NBE',
|
||||
'Al Baraka Bank Egypt B.S.C.'.tr: 'ABRK',
|
||||
'Egypt Post'.tr: 'POST',
|
||||
'Nasser Social Bank'.tr: 'NSB',
|
||||
'Industrial Development Bank'.tr: 'IDB',
|
||||
'Suez Canal Bank'.tr: 'SCB',
|
||||
'Mashreq Bank'.tr: 'MASHA',
|
||||
'Arab Investment Bank'.tr: 'AIB',
|
||||
'General Authority For Supply Commodities'.tr: 'GASCA',
|
||||
'Arab International Bank'.tr: 'AIB',
|
||||
'Agricultural Bank of Egypt'.tr: 'PDAC',
|
||||
'National Bank of Greece'.tr: 'NBG',
|
||||
'Central Bank Of Egypt'.tr: 'CBE',
|
||||
'ATTIJARIWAFA BANK Egypt'.tr: 'BBE',
|
||||
};
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
selectedBank = bankNames.values.first;
|
||||
}
|
||||
|
||||
void updateSelectedBank(String? bankShortName) {
|
||||
selectedBank = bankShortName ?? '';
|
||||
update();
|
||||
}
|
||||
|
||||
List<DropdownMenuItem<String>> getDropdownItems() {
|
||||
return bankNames.keys.map<DropdownMenuItem<String>>((bankFullName) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: bankNames[bankFullName],
|
||||
child: Text(bankFullName),
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
void showBankPicker(BuildContext context) {
|
||||
showCupertinoModalPopup(
|
||||
context: context,
|
||||
builder: (BuildContext context) => CupertinoActionSheet(
|
||||
title: Text('Select a Bank'.tr),
|
||||
actions: bankNames.keys.map((String bankFullName) {
|
||||
return CupertinoActionSheetAction(
|
||||
child: Text(bankFullName),
|
||||
onPressed: () {
|
||||
updateSelectedBank(bankNames[bankFullName]);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
cancelButton: CupertinoActionSheetAction(
|
||||
child: Text('Cancel'.tr),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BankDropdown extends StatelessWidget {
|
||||
final BankController bankController = Get.put(BankController());
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<BankController>(
|
||||
init: bankController,
|
||||
builder: (controller) {
|
||||
return CupertinoButton(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: () => controller.showBankPicker(context),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: CupertinoColors.systemGrey4),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
controller.selectedBank != null
|
||||
? controller.bankNames.keys.firstWhere(
|
||||
(key) =>
|
||||
controller.bankNames[key] ==
|
||||
controller.selectedBank,
|
||||
orElse: () => 'Select a Bank'.tr,
|
||||
)
|
||||
: 'Select a Bank'.tr,
|
||||
style: TextStyle(
|
||||
color: controller.selectedBank != null
|
||||
? CupertinoColors.black
|
||||
: CupertinoColors.systemGrey,
|
||||
),
|
||||
),
|
||||
const Icon(CupertinoIcons.chevron_down, size: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
299
lib/views/home/my_wallet/card_wallet_widget.dart
Executable file
299
lib/views/home/my_wallet/card_wallet_widget.dart
Executable file
@@ -0,0 +1,299 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../constant/box_name.dart';
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../constant/style.dart';
|
||||
import '../../../controller/home/payment/captain_wallet_controller.dart';
|
||||
import '../../../controller/home/payment/paymob_payout.dart';
|
||||
import '../../../main.dart';
|
||||
import '../../widgets/elevated_btn.dart';
|
||||
import '../../widgets/my_textField.dart';
|
||||
|
||||
// تذكير: ستحتاج إلى إضافة حزمة flutter_svg إلى ملف pubspec.yaml
|
||||
// dependencies:
|
||||
// flutter_svg: ^2.0.7
|
||||
|
||||
/// بطاقة المحفظة بتصميم سوري فاخر مستوحى من فن الأرابيسك والفسيفساء
|
||||
class CardSeferWalletDriver extends StatelessWidget {
|
||||
const CardSeferWalletDriver({super.key});
|
||||
|
||||
// SVG لنقشة أرابيسك هندسية لاستخدامها كخلفية
|
||||
final String arabesquePattern = '''
|
||||
<svg width="100%" height="100%" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<pattern id="arabesque" patternUnits="userSpaceOnUse" width="50" height="50" patternTransform="scale(1.2)">
|
||||
<g fill="#E7C582" fill-opacity="0.1">
|
||||
<path d="M25 0 L35.35 9.65 L50 25 L35.35 40.35 L25 50 L14.65 40.35 L0 25 L14.65 9.65 Z"/>
|
||||
<path d="M50 0 L60.35 9.65 L75 25 L60.35 40.35 L50 50 L39.65 40.35 L25 25 L39.65 9.65 Z"/>
|
||||
<path d="M0 50 L9.65 39.65 L25 25 L9.65 10.35 L0 0 L-9.65 10.35 L-25 25 L-9.65 39.65 Z"/>
|
||||
</g>
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="url(#arabesque)"/>
|
||||
</svg>
|
||||
''';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: GetBuilder<CaptainWalletController>(
|
||||
builder: (captainWalletController) {
|
||||
return GestureDetector(
|
||||
onTap: () => _showCashOutDialog(context, captainWalletController),
|
||||
child: Container(
|
||||
width: Get.width * 0.9,
|
||||
height: Get.height * 0.25,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: const Color(0xFF003C43).withOpacity(0.5),
|
||||
blurRadius: 25,
|
||||
spreadRadius: -5,
|
||||
offset: const Offset(0, 10),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
child: Stack(
|
||||
children: [
|
||||
// الخلفية الرئيسية
|
||||
Container(color: const Color(0xFF003C43)),
|
||||
// طبقة النقشة
|
||||
SvgPicture.string(arabesquePattern, fit: BoxFit.cover),
|
||||
// طبقة التأثير الزجاجي (Glassmorphism)
|
||||
BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 2.0, sigmaY: 2.0),
|
||||
child: Container(color: Colors.black.withOpacity(0.1)),
|
||||
),
|
||||
// محتوى البطاقة
|
||||
_buildCardContent(captainWalletController),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCardContent(CaptainWalletController captainWalletController) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 20, 24, 20),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'محفظة انطلق',
|
||||
style: AppStyle.headTitle.copyWith(
|
||||
fontFamily: 'Amiri', // خط يوحي بالفخامة
|
||||
color: Colors.white,
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
// أيقونة شريحة البطاقة
|
||||
const Icon(Icons.sim_card_outlined,
|
||||
color: Color(0xFFE7C582), size: 30),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
'الرصيد الحالي'.tr,
|
||||
style: AppStyle.title.copyWith(
|
||||
color: Colors.white.withOpacity(0.7),
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
// استخدام AnimatedSwitcher لإضافة حركة عند تحديث الرصيد
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 500),
|
||||
transitionBuilder: (child, animation) {
|
||||
return FadeTransition(opacity: animation, child: child);
|
||||
},
|
||||
child: Text(
|
||||
'${captainWalletController.totalAmountVisa} ${'ل.س'.tr}',
|
||||
key:
|
||||
ValueKey<String>(captainWalletController.totalAmountVisa),
|
||||
style: AppStyle.headTitle2.copyWith(
|
||||
color: const Color(0xFFE7C582), // Antique Gold
|
||||
fontSize: 40,
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
box.read(BoxName.nameDriver).toString().split(' ')[0],
|
||||
style: AppStyle.title.copyWith(
|
||||
color: Colors.white.withOpacity(0.9),
|
||||
fontSize: 16,
|
||||
letterSpacing: 0.5,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"سحب الرصيد".tr,
|
||||
style: AppStyle.title.copyWith(
|
||||
color: Colors.white.withOpacity(0.9),
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showCashOutDialog(
|
||||
BuildContext context, CaptainWalletController captainWalletController) {
|
||||
double minAmount = 20.0; // الحد الأدنى للسحب
|
||||
if (double.parse(captainWalletController.totalAmountVisa) >= minAmount) {
|
||||
Get.defaultDialog(
|
||||
barrierDismissible: false,
|
||||
title: 'هل تريد سحب أرباحك؟'.tr,
|
||||
titleStyle: AppStyle.title
|
||||
.copyWith(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
content: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.account_balance_wallet,
|
||||
color: AppColor.primaryColor, size: 30),
|
||||
const SizedBox(height: 15),
|
||||
Text(
|
||||
'${'رصيدك الإجمالي:'.tr} ${captainWalletController.totalAmountVisa} ${'ل.س'.tr}',
|
||||
style: AppStyle.title.copyWith(fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'طريقة الدفع:'.tr,
|
||||
style: AppStyle.title.copyWith(fontSize: 16),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
const MyDropDownSyria(),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Form(
|
||||
key: captainWalletController.formKey,
|
||||
child: MyTextForm(
|
||||
controller: captainWalletController.phoneWallet,
|
||||
label: "أدخل رقم محفظتك".tr,
|
||||
hint: "مثال: 0912345678".tr,
|
||||
type: TextInputType.phone,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
confirm: MyElevatedButton(
|
||||
title: 'تأكيد'.tr,
|
||||
onPressed: () async {
|
||||
if (captainWalletController.formKey.currentState!.validate()) {
|
||||
Get.back();
|
||||
String amountAfterFee =
|
||||
(double.parse(captainWalletController.totalAmountVisa) - 5)
|
||||
.toStringAsFixed(0);
|
||||
await Get.put(PaymobPayout()).payToWalletDriverAll(
|
||||
amountAfterFee,
|
||||
Get.find<SyrianPayoutController>().dropdownValue.toString(),
|
||||
captainWalletController.phoneWallet.text.toString(),
|
||||
);
|
||||
}
|
||||
},
|
||||
kolor: AppColor.greenColor,
|
||||
),
|
||||
cancel: MyElevatedButton(
|
||||
title: 'إلغاء'.tr,
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
kolor: AppColor.redColor,
|
||||
));
|
||||
} else {
|
||||
showCupertinoDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return CupertinoAlertDialog(
|
||||
title: Text("تنبيه".tr),
|
||||
content: Text(
|
||||
'${'المبلغ في محفظتك أقل من الحد الأدنى للسحب وهو'.tr} $minAmount ${'ل.س'.tr}',
|
||||
),
|
||||
actions: <Widget>[
|
||||
CupertinoDialogAction(
|
||||
isDefaultAction: true,
|
||||
child: Text("موافق".tr),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// هذا الكود من الملف الأصلي وهو ضروري لعمل الحوار
|
||||
class MyDropDownSyria extends StatelessWidget {
|
||||
const MyDropDownSyria({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(SyrianPayoutController());
|
||||
return GetBuilder<SyrianPayoutController>(builder: (controller) {
|
||||
return DropdownButton<String>(
|
||||
value: controller.dropdownValue,
|
||||
icon: const Icon(Icons.arrow_drop_down),
|
||||
elevation: 16,
|
||||
style: const TextStyle(color: Colors.deepPurple, fontSize: 16),
|
||||
underline: Container(
|
||||
height: 2,
|
||||
color: Colors.deepPurpleAccent,
|
||||
),
|
||||
onChanged: (String? newValue) {
|
||||
controller.changeValue(newValue);
|
||||
},
|
||||
items: <String>['syriatel', 'mtn']
|
||||
.map<DropdownMenuItem<String>>((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value.tr),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// هذا المتحكم ضروري لعمل القائمة المنسدلة
|
||||
class SyrianPayoutController extends GetxController {
|
||||
String dropdownValue = 'syriatel';
|
||||
|
||||
void changeValue(String? newValue) {
|
||||
if (newValue != null) {
|
||||
dropdownValue = newValue;
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
170
lib/views/home/my_wallet/ecash.dart
Normal file
170
lib/views/home/my_wallet/ecash.dart
Normal file
@@ -0,0 +1,170 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:local_auth/local_auth.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
import '../../../constant/box_name.dart';
|
||||
import '../../../constant/links.dart';
|
||||
import '../../../constant/style.dart';
|
||||
import '../../../controller/functions/crud.dart';
|
||||
import '../../../main.dart';
|
||||
|
||||
// --- ملاحظات هامة ---
|
||||
// 1. تأكد من إضافة الرابط الجديد إلى ملف AppLink الخاص بك:
|
||||
// static const String payWithEcashDriver = "$server/payment/payWithEcashDriver.php";
|
||||
//
|
||||
// 2. تأكد من أنك تخزن 'driverId' في الـ box الخاص بك، مثلاً:
|
||||
// box.read(BoxName.driverID)
|
||||
|
||||
/// دالة جديدة لبدء عملية الدفع للسائق عبر ecash
|
||||
Future<void> payWithEcashDriver(BuildContext context, String amount) async {
|
||||
try {
|
||||
// يمكنك استخدام نفس طريقة التحقق بالبصمة إذا أردت
|
||||
bool isAvailable = await LocalAuthentication().isDeviceSupported();
|
||||
if (isAvailable) {
|
||||
bool didAuthenticate = await LocalAuthentication().authenticate(
|
||||
localizedReason: 'Use Touch ID or Face ID to confirm payment'.tr,
|
||||
);
|
||||
|
||||
if (didAuthenticate) {
|
||||
// استدعاء الـ Endpoint الجديد على السيرفر الخاص بك
|
||||
var res = await CRUD().postWallet(
|
||||
link: AppLink.payWithEcashDriver,
|
||||
payload: {
|
||||
// أرسل البيانات التي يحتاجها السيرفر
|
||||
"amount": amount,
|
||||
// "driverId": box.read(BoxName.driverID), // تأكد من وجود هذا المتغير
|
||||
"driverId": box.read(BoxName.driverID), // تأكد من وجود هذا المتغير
|
||||
},
|
||||
);
|
||||
|
||||
// التأكد من أن السيرفر أعاد رابط الدفع بنجاح
|
||||
if (res != null &&
|
||||
res['status'] == 'success' &&
|
||||
res['message'] != null) {
|
||||
final String paymentUrl = res['message'];
|
||||
// الانتقال إلى شاشة الدفع الجديدة الخاصة بـ ecash للسائق
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
EcashDriverPaymentScreen(paymentUrl: paymentUrl),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// عرض رسالة خطأ في حال فشل السيرفر في إنشاء الرابط
|
||||
Get.defaultDialog(
|
||||
title: 'Error'.tr,
|
||||
content: Text(
|
||||
'Failed to initiate payment. Please try again.'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
Get.defaultDialog(
|
||||
title: 'Error'.tr,
|
||||
content: Text(
|
||||
'An error occurred during the payment process.'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// شاشة جديدة ومبسطة خاصة بدفع السائقين عبر ecash
|
||||
class EcashDriverPaymentScreen extends StatefulWidget {
|
||||
final String paymentUrl;
|
||||
|
||||
const EcashDriverPaymentScreen({required this.paymentUrl, Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<EcashDriverPaymentScreen> createState() =>
|
||||
_EcashDriverPaymentScreenState();
|
||||
}
|
||||
|
||||
class _EcashDriverPaymentScreenState extends State<EcashDriverPaymentScreen> {
|
||||
late final WebViewController _controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..setNavigationDelegate(NavigationDelegate(
|
||||
onPageFinished: (url) {
|
||||
print('Ecash Driver WebView URL Finished: $url');
|
||||
|
||||
// هنا نتحقق فقط من أن المستخدم عاد إلى صفحة النجاح
|
||||
// لا حاجة لاستدعاء أي API هنا، فالـ Webhook يقوم بكل العمل
|
||||
if (url.contains("success.php")) {
|
||||
showProcessingDialog();
|
||||
}
|
||||
},
|
||||
))
|
||||
..loadRequest(Uri.parse(widget.paymentUrl));
|
||||
}
|
||||
|
||||
// دالة لعرض رسالة "العملية قيد المعالجة"
|
||||
void showProcessingDialog() {
|
||||
showDialog(
|
||||
barrierDismissible: false,
|
||||
context: Get.context!,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
title: Row(
|
||||
children: [
|
||||
Icon(Icons.check_circle, color: Colors.green),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
"Payment Successful".tr,
|
||||
style: TextStyle(
|
||||
color: Colors.green,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
content: Text(
|
||||
"Your payment is being processed and your wallet will be updated shortly."
|
||||
.tr,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
// أغلق مربع الحوار، ثم أغلق شاشة الدفع
|
||||
Navigator.pop(context); // Close the dialog
|
||||
Navigator.pop(context); // Close the payment screen
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: Colors.green,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
"OK".tr,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text('Complete Payment'.tr)),
|
||||
body: WebViewWidget(controller: _controller),
|
||||
);
|
||||
}
|
||||
}
|
||||
54
lib/views/home/my_wallet/payment_history_driver_page.dart
Executable file
54
lib/views/home/my_wallet/payment_history_driver_page.dart
Executable file
@@ -0,0 +1,54 @@
|
||||
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/views/widgets/my_scafold.dart';
|
||||
import 'package:sefer_driver/views/widgets/mycircular.dart';
|
||||
|
||||
import '../../../controller/payment/driver_payment_controller.dart';
|
||||
|
||||
class PaymentHistoryDriverPage extends StatelessWidget {
|
||||
const PaymentHistoryDriverPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(DriverWalletHistoryController());
|
||||
return MyScafolld(
|
||||
title: 'Payment History'.tr,
|
||||
body: [
|
||||
GetBuilder<DriverWalletHistoryController>(
|
||||
builder: (controller) => controller.isLoading
|
||||
? const MyCircularProgressIndicator()
|
||||
: ListView.builder(
|
||||
itemCount: controller.archive.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
var list = controller.archive[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: double.parse(list['amount']) < 0
|
||||
? AppColor.redColor.withOpacity(.4)
|
||||
: AppColor.greenColor.withOpacity(.4)),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
list['amount'],
|
||||
style: AppStyle.title,
|
||||
),
|
||||
Text(
|
||||
list['created_at'],
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
isleading: true);
|
||||
}
|
||||
}
|
||||
601
lib/views/home/my_wallet/points_captain.dart
Executable file
601
lib/views/home/my_wallet/points_captain.dart
Executable file
@@ -0,0 +1,601 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:local_auth/local_auth.dart';
|
||||
import 'package:sefer_driver/constant/colors.dart';
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
import 'package:sefer_driver/controller/home/payment/captain_wallet_controller.dart';
|
||||
import 'package:sefer_driver/controller/payment/payment_controller.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
import '../../../constant/box_name.dart';
|
||||
import '../../../constant/links.dart';
|
||||
import '../../../controller/functions/crud.dart';
|
||||
import '../../../main.dart';
|
||||
import '../../widgets/elevated_btn.dart';
|
||||
import '../../widgets/my_textField.dart';
|
||||
import 'ecash.dart';
|
||||
|
||||
class PointsCaptain extends StatelessWidget {
|
||||
PaymentController paymentController = Get.put(PaymentController());
|
||||
CaptainWalletController captainWalletController =
|
||||
Get.put(CaptainWalletController());
|
||||
|
||||
PointsCaptain({
|
||||
super.key,
|
||||
required this.kolor,
|
||||
required this.countPoint,
|
||||
required this.pricePoint,
|
||||
});
|
||||
final Color kolor;
|
||||
final String countPoint;
|
||||
double pricePoint;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: () async {
|
||||
Get.defaultDialog(
|
||||
title: 'Which method you will pay'.tr,
|
||||
titleStyle: AppStyle.title,
|
||||
content: Column(
|
||||
children: [
|
||||
Text(
|
||||
'${'you can buy '.tr}$countPoint ${'L.S'.tr}${'by '.tr}${'$pricePoint'.tr}',
|
||||
style: AppStyle.title,
|
||||
),
|
||||
MyElevatedButton(
|
||||
title: 'Pay with Credit Card'.tr,
|
||||
onPressed: () async {
|
||||
Get.back();
|
||||
payWithEcashDriver(context, pricePoint.toString());
|
||||
|
||||
// var d = jsonDecode(res);
|
||||
}, //51524
|
||||
),
|
||||
// Add some spacing between buttons
|
||||
MyElevatedButton(
|
||||
kolor: AppColor.redColor,
|
||||
title: 'Pay with Wallet'.tr,
|
||||
onPressed: () async {
|
||||
Get.back();
|
||||
Get.defaultDialog(
|
||||
barrierDismissible: false,
|
||||
title: 'Insert Wallet phone number'.tr,
|
||||
content: Form(
|
||||
key: paymentController.formKey,
|
||||
child: MyTextForm(
|
||||
controller:
|
||||
paymentController.walletphoneController,
|
||||
label: 'Insert Wallet phone number'.tr,
|
||||
hint: 'Insert Wallet phone number'.tr,
|
||||
type: TextInputType.phone)),
|
||||
confirm: MyElevatedButton(
|
||||
title: 'OK'.tr,
|
||||
onPressed: () async {
|
||||
Get.back();
|
||||
if (paymentController.formKey.currentState!
|
||||
.validate()) {
|
||||
box.write(
|
||||
BoxName.phoneWallet,
|
||||
paymentController
|
||||
.walletphoneController.text);
|
||||
await payWithMTNWallet(
|
||||
context, pricePoint.toString(), 'SYP');
|
||||
}
|
||||
}));
|
||||
},
|
||||
),
|
||||
],
|
||||
));
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 3, vertical: 8),
|
||||
child: Container(
|
||||
width: Get.width * .22,
|
||||
height: Get.width * .22,
|
||||
margin: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
kolor.withOpacity(0.3),
|
||||
kolor,
|
||||
kolor.withOpacity(0.7),
|
||||
kolor,
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
border: Border.all(color: AppColor.accentColor),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
shape: BoxShape.rectangle,
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Text(
|
||||
'$countPoint ${'L.S'.tr}',
|
||||
style: AppStyle.subtitle
|
||||
.copyWith(color: AppColor.secondaryColor),
|
||||
),
|
||||
Text(
|
||||
'$pricePoint ${'L.S'.tr}',
|
||||
style:
|
||||
AppStyle.title.copyWith(color: AppColor.secondaryColor),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PaymentScreen extends StatefulWidget {
|
||||
final String iframeUrl;
|
||||
final String countPrice;
|
||||
|
||||
const PaymentScreen(
|
||||
{required this.iframeUrl, Key? key, required this.countPrice})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<PaymentScreen> createState() => _PaymentScreenState();
|
||||
}
|
||||
|
||||
class _PaymentScreenState extends State<PaymentScreen> {
|
||||
late final WebViewController _controller;
|
||||
final controller = Get.find<CaptainWalletController>();
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_controller = WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..setNavigationDelegate(NavigationDelegate(
|
||||
onPageFinished: (url) {
|
||||
if (url.contains("success")) {
|
||||
_fetchPaymentStatus(); // ✅ استدعاء الويب هوك بعد نجاح الدفع
|
||||
} else if (url.contains("failed")) {
|
||||
showCustomDialog(
|
||||
title: "Error".tr,
|
||||
message: 'Payment Failed'.tr, // يتم جلب رسالة الخطأ من الخادم
|
||||
isSuccess: false,
|
||||
);
|
||||
}
|
||||
},
|
||||
))
|
||||
..loadRequest(Uri.parse(widget.iframeUrl));
|
||||
}
|
||||
|
||||
Future<void> _fetchPaymentStatus() async {
|
||||
final String userId = box.read(BoxName.phoneDriver);
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
|
||||
try {
|
||||
final response = await CRUD().postWallet(
|
||||
link: AppLink.paymetVerifyDriver,
|
||||
payload: {
|
||||
'user_id': userId,
|
||||
'driverID': box.read(BoxName.driverID),
|
||||
'paymentMethod': 'visa-in',
|
||||
},
|
||||
);
|
||||
|
||||
if (response != 'failure' && response != 'token_expired') {
|
||||
if (response['status'] == 'success') {
|
||||
final payment = response['message'];
|
||||
final amount = payment['amount'].toString();
|
||||
final bonus = payment['bonus'].toString();
|
||||
final paymentID = payment['paymentID'].toString();
|
||||
|
||||
await controller.getCaptainWalletFromBuyPoints();
|
||||
|
||||
showCustomDialog(
|
||||
title: "payment_success".tr,
|
||||
message:
|
||||
"${"transaction_id".tr}: $paymentID\n${"amount_paid".tr}: $amount EGP\n${"bonus_added".tr}: $bonus ${"points".tr}",
|
||||
isSuccess: true,
|
||||
);
|
||||
} else {
|
||||
showCustomDialog(
|
||||
title: "transaction_failed".tr,
|
||||
message: response['message'].toString(),
|
||||
isSuccess: false,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
showCustomDialog(
|
||||
title: "connection_failed".tr,
|
||||
message: response.toString(),
|
||||
isSuccess: false,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
showCustomDialog(
|
||||
title: "server_error".tr,
|
||||
message: "server_error_message".tr,
|
||||
isSuccess: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void showCustomDialog({
|
||||
required String title,
|
||||
required String message,
|
||||
required bool isSuccess,
|
||||
}) {
|
||||
showDialog(
|
||||
barrierDismissible: false,
|
||||
context: Get.context!,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
title: Row(
|
||||
children: [
|
||||
Icon(
|
||||
isSuccess ? Icons.check_circle : Icons.error,
|
||||
color: isSuccess ? Colors.green : Colors.red,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
color: isSuccess ? Colors.green : Colors.red,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
content: Text(
|
||||
message,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: isSuccess ? Colors.green : Colors.red,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
"OK",
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('إتمام الدفع')),
|
||||
body: WebViewWidget(controller: _controller),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PaymentScreenWallet extends StatefulWidget {
|
||||
final String iframeUrl;
|
||||
final String countPrice;
|
||||
|
||||
const PaymentScreenWallet(
|
||||
{required this.iframeUrl, Key? key, required this.countPrice})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<PaymentScreenWallet> createState() => _PaymentScreenWalletState();
|
||||
}
|
||||
|
||||
class _PaymentScreenWalletState extends State<PaymentScreenWallet> {
|
||||
late final WebViewController _controller;
|
||||
final controller = Get.find<CaptainWalletController>();
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_controller = WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..setNavigationDelegate(NavigationDelegate(
|
||||
onPageFinished: (url) {
|
||||
if (url.contains("success")) {
|
||||
_fetchPaymentStatus(); // ✅ استدعاء الويب هوك بعد نجاح الدفع
|
||||
} else if (url.contains("failed")) {
|
||||
showCustomDialog(
|
||||
title: "Error".tr,
|
||||
message: 'Payment Failed'.tr, // يتم جلب رسالة الخطأ من الخادم
|
||||
isSuccess: false,
|
||||
);
|
||||
}
|
||||
},
|
||||
))
|
||||
..loadRequest(Uri.parse(widget.iframeUrl));
|
||||
}
|
||||
|
||||
Future<void> _fetchPaymentStatus() async {
|
||||
final String userId = '+963' + box.read(BoxName.phoneWallet);
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
|
||||
try {
|
||||
final response = await CRUD().postWallet(
|
||||
link: AppLink.paymetVerifyDriver,
|
||||
payload: {
|
||||
'user_id': userId,
|
||||
'driverID': box.read(BoxName.driverID),
|
||||
'paymentMethod': 'visa-in',
|
||||
},
|
||||
);
|
||||
|
||||
if (response != 'failure' && response != 'token_expired') {
|
||||
if (response['status'] == 'success') {
|
||||
final payment = response['message'];
|
||||
final amount = payment['amount'].toString();
|
||||
final bonus = payment['bonus'].toString();
|
||||
final paymentID = payment['paymentID'].toString();
|
||||
|
||||
await controller.getCaptainWalletFromBuyPoints();
|
||||
|
||||
showCustomDialog(
|
||||
title: "payment_success".tr,
|
||||
message:
|
||||
"${"transaction_id".tr}: $paymentID\n${"amount_paid".tr}: $amount EGP\n${"bonus_added".tr}: $bonus ${"points".tr}",
|
||||
isSuccess: true,
|
||||
);
|
||||
} else {
|
||||
showCustomDialog(
|
||||
title: "transaction_failed".tr,
|
||||
message: response['message'].toString(),
|
||||
isSuccess: false,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
showCustomDialog(
|
||||
title: "connection_failed".tr,
|
||||
message: response.toString(),
|
||||
isSuccess: false,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
showCustomDialog(
|
||||
title: "server_error".tr,
|
||||
message: "server_error_message".tr,
|
||||
isSuccess: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void showCustomDialog({
|
||||
required String title,
|
||||
required String message,
|
||||
required bool isSuccess,
|
||||
}) {
|
||||
showDialog(
|
||||
barrierDismissible: false,
|
||||
context: Get.context!,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
title: Row(
|
||||
children: [
|
||||
Icon(
|
||||
isSuccess ? Icons.check_circle : Icons.error,
|
||||
color: isSuccess ? Colors.green : Colors.red,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
color: isSuccess ? Colors.green : Colors.red,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
content: Text(
|
||||
message,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: isSuccess ? Colors.green : Colors.red,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
"OK",
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('إتمام الدفع')),
|
||||
body: WebViewWidget(controller: _controller),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> payWithMTNWallet(
|
||||
BuildContext context, String amount, String currency) async {
|
||||
// استخدام مؤشر تحميل لتجربة مستخدم أفضل
|
||||
Get.dialog(const Center(child: CircularProgressIndicator()),
|
||||
barrierDismissible: false);
|
||||
|
||||
try {
|
||||
String phone = box.read(BoxName.phoneWallet) ?? '963992952235';
|
||||
String driverID = box.read(BoxName.driverID).toString();
|
||||
String formattedAmount = double.parse(amount).toStringAsFixed(0);
|
||||
|
||||
print("🚀 بدء عملية دفع MTN");
|
||||
print(
|
||||
"📦 Payload: driverID: $driverID, amount: $formattedAmount, phone: $phone");
|
||||
|
||||
// التحقق من البصمة (اختياري)
|
||||
bool isAuthSupported = await LocalAuthentication().isDeviceSupported();
|
||||
if (isAuthSupported) {
|
||||
bool didAuthenticate = await LocalAuthentication().authenticate(
|
||||
localizedReason: 'استخدم بصمة الإصبع أو الوجه لتأكيد الدفع',
|
||||
);
|
||||
if (!didAuthenticate) {
|
||||
if (Get.isDialogOpen ?? false) Get.back();
|
||||
print("❌ المستخدم لم يؤكد بالبصمة/الوجه");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 1️⃣ استدعاء mtn_start_payment.php (الملف الجديد)
|
||||
var responseData = await CRUD().postWallet(
|
||||
link: AppLink.payWithMTNStart,
|
||||
payload: {
|
||||
"amount": formattedAmount,
|
||||
"passengerId": driverID,
|
||||
"phone": phone,
|
||||
},
|
||||
);
|
||||
|
||||
print("✅ استجابة الخادم (mtn_start_payment.php):");
|
||||
print(responseData);
|
||||
|
||||
// --- بداية التعديل المهم ---
|
||||
// التحقق القوي من الاستجابة لتجنب الأخطاء
|
||||
Map<String, dynamic> startRes;
|
||||
|
||||
if (responseData is Map<String, dynamic>) {
|
||||
// إذا كانت الاستجابة بالفعل Map، استخدمها مباشرة
|
||||
startRes = responseData;
|
||||
} else if (responseData is String) {
|
||||
// إذا كانت نص، حاول تحليلها كـ JSON
|
||||
try {
|
||||
startRes = json.decode(responseData);
|
||||
} catch (e) {
|
||||
throw Exception(
|
||||
"فشل في تحليل استجابة الخادم. الاستجابة: $responseData");
|
||||
}
|
||||
} else {
|
||||
// نوع غير متوقع
|
||||
throw Exception("تم استلام نوع بيانات غير متوقع من الخادم.");
|
||||
}
|
||||
|
||||
if (startRes['status'] != 'success') {
|
||||
String errorMsg = startRes['message']?.toString() ??
|
||||
"فشل بدء عملية الدفع. حاول مرة أخرى.";
|
||||
throw Exception(errorMsg);
|
||||
}
|
||||
// --- نهاية التعديل المهم ---
|
||||
|
||||
// استخراج البيانات بأمان
|
||||
final messageData = startRes["message"];
|
||||
final invoiceNumber = messageData["invoiceNumber"].toString();
|
||||
final operationNumber = messageData["operationNumber"].toString();
|
||||
final guid = messageData["guid"].toString();
|
||||
|
||||
print(
|
||||
"📄 invoiceNumber: $invoiceNumber, 🔢 operationNumber: $operationNumber, 🧭 guid: $guid");
|
||||
|
||||
if (Get.isDialogOpen ?? false)
|
||||
Get.back(); // إغلاق مؤشر التحميل قبل عرض حوار OTP
|
||||
|
||||
// 2️⃣ عرض واجهة إدخال OTP
|
||||
String? otp = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
String input = "";
|
||||
return AlertDialog(
|
||||
title: const Text("أدخل كود التحقق"),
|
||||
content: TextField(
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: const InputDecoration(hintText: "كود OTP"),
|
||||
onChanged: (val) => input = val,
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: const Text("تأكيد"),
|
||||
onPressed: () => Navigator.of(context).pop(input),
|
||||
),
|
||||
TextButton(
|
||||
child: const Text("إلغاء"),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (otp == null || otp.isEmpty) {
|
||||
print("❌ لم يتم إدخال OTP");
|
||||
return;
|
||||
}
|
||||
print("🔐 تم إدخال OTP: $otp");
|
||||
|
||||
Get.dialog(const Center(child: CircularProgressIndicator()),
|
||||
barrierDismissible: false);
|
||||
|
||||
// 3️⃣ استدعاء mtn_confirm.php
|
||||
var confirmRes = await CRUD().postWallet(
|
||||
link: AppLink.payWithMTNConfirm,
|
||||
payload: {
|
||||
"invoiceNumber": invoiceNumber,
|
||||
"operationNumber": operationNumber,
|
||||
"guid": guid,
|
||||
"otp": otp,
|
||||
"phone": phone,
|
||||
},
|
||||
);
|
||||
|
||||
if (Get.isDialogOpen ?? false) Get.back();
|
||||
|
||||
print("✅ استجابة mtn_confirm.php:");
|
||||
print(confirmRes);
|
||||
|
||||
if (confirmRes != null && confirmRes['status'] == 'success') {
|
||||
Get.defaultDialog(
|
||||
title: "✅ نجاح",
|
||||
content: const Text("تمت عملية الدفع وإضافة الرصيد إلى محفظتك."),
|
||||
);
|
||||
} else {
|
||||
String errorMsg =
|
||||
confirmRes?['message']?.toString() ?? "فشل في تأكيد الدفع";
|
||||
Get.defaultDialog(
|
||||
title: "❌ فشل",
|
||||
content: Text(errorMsg),
|
||||
);
|
||||
}
|
||||
} catch (e, s) {
|
||||
print("🔥 خطأ أثناء الدفع عبر MTN:");
|
||||
print(e);
|
||||
print(s);
|
||||
if (Get.isDialogOpen ?? false) Get.back();
|
||||
Get.defaultDialog(
|
||||
title: 'حدث خطأ',
|
||||
content: Text(e.toString().replaceFirst("Exception: ", "")),
|
||||
);
|
||||
}
|
||||
}
|
||||
145
lib/views/home/my_wallet/transfer_budget_page.dart
Executable file
145
lib/views/home/my_wallet/transfer_budget_page.dart
Executable file
@@ -0,0 +1,145 @@
|
||||
import 'package:sefer_driver/constant/style.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:sefer_driver/views/widgets/mydialoug.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../controller/home/payment/captain_wallet_controller.dart';
|
||||
|
||||
class TransferBudgetPage extends StatelessWidget {
|
||||
const TransferBudgetPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(CaptainWalletController());
|
||||
return MyScafolld(
|
||||
title: "Transfer budget".tr,
|
||||
body: [
|
||||
GetBuilder<CaptainWalletController>(
|
||||
builder: (captainWalletController) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Container(
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
height: Get.height * .7,
|
||||
width: double.infinity,
|
||||
child: Form(
|
||||
key: captainWalletController.formKeyTransfer,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
MyTextForm(
|
||||
controller: captainWalletController
|
||||
.newDriverPhoneController,
|
||||
label: 'phone number of driver'.tr,
|
||||
hint: 'phone number of driver',
|
||||
type: TextInputType.phone),
|
||||
MyTextForm(
|
||||
controller: captainWalletController
|
||||
.amountFromBudgetController,
|
||||
label: 'insert amount'.tr,
|
||||
hint:
|
||||
'${'You have in account'.tr} ${captainWalletController.totalAmountVisa}',
|
||||
type: TextInputType.number),
|
||||
captainWalletController.isNewTransfer
|
||||
? const MyCircularProgressIndicator()
|
||||
: captainWalletController
|
||||
.amountToNewDriverMap.isEmpty
|
||||
? MyElevatedButton(
|
||||
title: 'Next'.tr,
|
||||
onPressed: () async {
|
||||
await captainWalletController
|
||||
.detectNewDriverFromMyBudget();
|
||||
})
|
||||
: const SizedBox(),
|
||||
captainWalletController.amountToNewDriverMap.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: double.maxFinite,
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
child: Text(
|
||||
'Name :'.tr +
|
||||
captainWalletController
|
||||
.amountToNewDriverMap[0]['name']
|
||||
.toString(),
|
||||
textAlign: TextAlign.center,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
Container(
|
||||
width: double.maxFinite,
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
child: Text(
|
||||
"${"NationalID".tr} ${captainWalletController.amountToNewDriverMap[0]['national_number']}",
|
||||
style: AppStyle.title,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
Container(
|
||||
width: double.maxFinite,
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
child: Text(
|
||||
"${"amount".tr} ${captainWalletController.amountFromBudgetController.text} ${'LE'.tr}",
|
||||
style: AppStyle.title,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
captainWalletController
|
||||
.amountToNewDriverMap.isNotEmpty
|
||||
? MyElevatedButton(
|
||||
title: 'Transfer'.tr,
|
||||
onPressed: () async {
|
||||
if (double.parse(
|
||||
captainWalletController
|
||||
.amountFromBudgetController
|
||||
.text) <
|
||||
double.parse(
|
||||
captainWalletController
|
||||
.totalAmountVisa) -
|
||||
5) {
|
||||
await captainWalletController
|
||||
.addTransferDriversWallet(
|
||||
'TransferFrom',
|
||||
'TransferTo',
|
||||
);
|
||||
} else {
|
||||
MyDialog().getDialog(
|
||||
"You dont have money in your Wallet"
|
||||
.tr,
|
||||
"You dont have money in your Wallet or you should less transfer 5 LE to activate"
|
||||
.tr, () {
|
||||
Get.back();
|
||||
});
|
||||
}
|
||||
})
|
||||
: const SizedBox()
|
||||
],
|
||||
),
|
||||
)
|
||||
: const SizedBox()
|
||||
],
|
||||
)),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
isleading: true);
|
||||
}
|
||||
}
|
||||
610
lib/views/home/my_wallet/walet_captain.dart
Executable file
610
lib/views/home/my_wallet/walet_captain.dart
Executable file
@@ -0,0 +1,610 @@
|
||||
import 'package:local_auth/local_auth.dart';
|
||||
import 'package:sefer_driver/constant/links.dart';
|
||||
import 'package:sefer_driver/controller/functions/crud.dart';
|
||||
import 'package:sefer_driver/controller/functions/tts.dart';
|
||||
import 'package:sefer_driver/views/home/my_wallet/payment_history_driver_page.dart';
|
||||
import 'package:sefer_driver/views/widgets/error_snakbar.dart';
|
||||
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/constant/box_name.dart';
|
||||
import 'package:sefer_driver/constant/colors.dart';
|
||||
import 'package:sefer_driver/constant/info.dart';
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
import 'package:sefer_driver/controller/home/payment/captain_wallet_controller.dart';
|
||||
import 'package:sefer_driver/main.dart';
|
||||
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
|
||||
import 'package:sefer_driver/views/widgets/my_textField.dart';
|
||||
import 'package:sefer_driver/views/widgets/mycircular.dart';
|
||||
|
||||
import '../../../controller/payment/driver_payment_controller.dart';
|
||||
import '../../widgets/my_scafold.dart';
|
||||
import 'card_wallet_widget.dart';
|
||||
import 'points_captain.dart';
|
||||
import 'transfer_budget_page.dart';
|
||||
import 'weekly_payment_page.dart';
|
||||
|
||||
class WalletCaptainRefactored extends StatelessWidget {
|
||||
WalletCaptainRefactored({super.key});
|
||||
|
||||
final CaptainWalletController captainWalletController =
|
||||
Get.put(CaptainWalletController());
|
||||
|
||||
// دالة مساعدة لتحديد لون خلفية النقاط
|
||||
Color _getPointsColor(String pointsStr) {
|
||||
final points = double.tryParse(pointsStr) ?? 0.0;
|
||||
if (points < -30000) {
|
||||
return AppColor.redColor;
|
||||
} else if (points < 0 && points >= -30000) {
|
||||
return AppColor.yellowColor;
|
||||
} else {
|
||||
return AppColor.greenColor;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
captainWalletController.refreshCaptainWallet();
|
||||
return MyScafolld(
|
||||
title: 'Driver Wallet'.tr,
|
||||
isleading: true,
|
||||
action: IconButton(
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: () => captainWalletController.refreshCaptainWallet(),
|
||||
tooltip: 'Refresh'.tr,
|
||||
),
|
||||
body: [
|
||||
GetBuilder<CaptainWalletController>(
|
||||
builder: (controller) {
|
||||
if (controller.isLoading) {
|
||||
return const MyCircularProgressIndicator();
|
||||
}
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildTotalPointsSection(context, controller),
|
||||
const SizedBox(height: 16),
|
||||
const CardSeferWalletDriver(), // This can be redesigned if needed
|
||||
const SizedBox(height: 16),
|
||||
_buildWalletDetailsCard(context, controller),
|
||||
const SizedBox(height: 24),
|
||||
_buildPromoSection(controller),
|
||||
const SizedBox(height: 24),
|
||||
_buildNavigationButtons(),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// القسم العلوي لعرض النقاط الإجمالية
|
||||
Widget _buildTotalPointsSection(
|
||||
BuildContext context, CaptainWalletController controller) {
|
||||
return Card(
|
||||
elevation: 4,
|
||||
color: _getPointsColor(controller.totalPoints.toString()),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
showCupertinoDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) => CupertinoAlertDialog(
|
||||
title: Text('Info'.tr),
|
||||
content: Text(
|
||||
'The 30000 points equal 30000 S.P for you \nSo go and gain your money'
|
||||
.tr),
|
||||
actions: [
|
||||
CupertinoDialogAction(
|
||||
child: Text("OK".tr),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 8),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
'${'Total Points is'.tr} 💎',
|
||||
style: AppStyle.headTitle2
|
||||
.copyWith(color: Colors.white, fontWeight: FontWeight.bold),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
controller.totalPoints.toString(),
|
||||
style: AppStyle.headTitle2.copyWith(
|
||||
color: Colors.white,
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w900),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
if (double.parse(controller.totalPoints.toString()) < -30000)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12.0),
|
||||
child: CupertinoButton(
|
||||
color: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
onPressed: () {
|
||||
// Add your charge account logic here
|
||||
},
|
||||
child: Text(
|
||||
'Charge your Account'.tr,
|
||||
style: TextStyle(
|
||||
color: AppColor.redColor,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// بطاقة لعرض تفاصيل المحفظة وخيارات الشراء
|
||||
Widget _buildWalletDetailsCard(
|
||||
BuildContext context, CaptainWalletController controller) {
|
||||
return Card(
|
||||
elevation: 4,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_BudgetInfoRow(
|
||||
title: 'Total Budget from trips is '.tr,
|
||||
amount: controller.totalAmount,
|
||||
onTap: () {
|
||||
Get.snackbar(
|
||||
icon: InkWell(
|
||||
onTap: () async => await Get.put(TextToSpeechController())
|
||||
.speakText(
|
||||
'This amount for all trip I get from Passengers'
|
||||
.tr),
|
||||
child: const Icon(Icons.headphones)),
|
||||
'${'Total Amount:'.tr} ${controller.totalAmount} ${'S.P'.tr}',
|
||||
'This amount for all trip I get from Passengers'.tr,
|
||||
duration: const Duration(seconds: 6),
|
||||
backgroundColor: AppColor.yellowColor,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
},
|
||||
),
|
||||
const Divider(height: 32),
|
||||
_BudgetInfoRow(
|
||||
title: 'Total Budget from trips by\nCredit card is '.tr,
|
||||
amount: controller.totalAmountVisa,
|
||||
onTap: () {
|
||||
Get.snackbar(
|
||||
icon: InkWell(
|
||||
onTap: () async => await Get.find<TextToSpeechController>()
|
||||
.speakText(
|
||||
'This amount for all trip I get from Passengers and Collected For me in'
|
||||
.tr +
|
||||
' SAFAR Wallet'.tr),
|
||||
child: const Icon(Icons.headphones),
|
||||
),
|
||||
'${'Total Amount:'.tr} ${controller.totalAmountVisa} ${'S.P'.tr}',
|
||||
'This amount for all trip I get from Passengers and Collected For me in'
|
||||
.tr +
|
||||
' ${AppInformation.appName} Wallet'.tr,
|
||||
duration: const Duration(seconds: 6),
|
||||
backgroundColor: AppColor.redColor,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
_buildBuyPointsButton(controller),
|
||||
const SizedBox(height: 16),
|
||||
// _buildTransferBudgetButton(controller), // Uncomment if needed
|
||||
_buildPurchaseInstructions(),
|
||||
const SizedBox(height: 8),
|
||||
_buildPointsOptions(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// قسم العروض الترويجية
|
||||
Widget _buildPromoSection(CaptainWalletController controller) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Today's Promo".tr, style: AppStyle.headTitle),
|
||||
const SizedBox(height: 10),
|
||||
_PromoProgressCard(
|
||||
title: 'Morning Promo'.tr,
|
||||
timePromo: 'Morning Promo',
|
||||
count: (controller.walletDate['message'][0]['morning_count']),
|
||||
maxCount: 5,
|
||||
description:
|
||||
"this is count of your all trips in the morning promo today from 7:00am to 10:00am"
|
||||
.tr,
|
||||
controller: controller,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_PromoProgressCard(
|
||||
title: 'Afternoon Promo'.tr,
|
||||
timePromo: 'Afternoon Promo',
|
||||
count: (controller.walletDate['message'][0]['afternoon_count']),
|
||||
maxCount: 5,
|
||||
description:
|
||||
"this is count of your all trips in the Afternoon promo today from 3:00pm to 6:00 pm"
|
||||
.tr,
|
||||
controller: controller,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// أزرار التنقل السفلية
|
||||
Widget _buildNavigationButtons() {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: MyElevatedButton(
|
||||
kolor: AppColor.blueColor,
|
||||
title: 'Payment History'.tr,
|
||||
onPressed: () async {
|
||||
await Get.put(DriverWalletHistoryController())
|
||||
.getArchivePayment();
|
||||
Get.to(() => const PaymentHistoryDriverPage(),
|
||||
transition: Transition.size);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: MyElevatedButton(
|
||||
kolor: AppColor.blueColor,
|
||||
title: 'Weekly Budget'.tr,
|
||||
onPressed: () async {
|
||||
await Get.put(DriverWalletHistoryController())
|
||||
.getWeekllyArchivePayment();
|
||||
Get.to(() => const WeeklyPaymentPage(),
|
||||
transition: Transition.size);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// --- حافظت على هذه الدوال كما هي لأنها تحتوي على منطق مهم ---
|
||||
Widget _buildBuyPointsButton(CaptainWalletController controller) {
|
||||
return MyElevatedButton(
|
||||
title: 'You can buy points from your budget'.tr,
|
||||
onPressed: () {
|
||||
Get.defaultDialog(
|
||||
title: 'Pay from my budget'.tr,
|
||||
content: Form(
|
||||
key: controller.formKey,
|
||||
child: MyTextForm(
|
||||
controller: controller.amountFromBudgetController,
|
||||
label:
|
||||
'${'You have in account'.tr} ${controller.totalAmountVisa}',
|
||||
hint: '${'You have in account'.tr} ${controller.totalAmountVisa}',
|
||||
type: TextInputType.number,
|
||||
),
|
||||
),
|
||||
confirm: MyElevatedButton(
|
||||
title: 'Pay'.tr,
|
||||
onPressed: () async {
|
||||
bool isAvailable =
|
||||
await LocalAuthentication().isDeviceSupported();
|
||||
if (isAvailable) {
|
||||
// Authenticate the user
|
||||
bool didAuthenticate = await LocalAuthentication().authenticate(
|
||||
localizedReason:
|
||||
'Use Touch ID or Face ID to confirm payment'.tr,
|
||||
options: const AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
));
|
||||
if (didAuthenticate) {
|
||||
if (double.parse(controller.amountFromBudgetController.text) <
|
||||
double.parse(controller.totalAmountVisa)) {
|
||||
await controller.payFromBudget();
|
||||
} else {
|
||||
Get.back();
|
||||
|
||||
mySnackeBarError('Your Budget less than needed'.tr);
|
||||
}
|
||||
} else {
|
||||
// Authentication failed, handle accordingly
|
||||
MyDialog().getDialog('Authentication failed'.tr, ''.tr, () {
|
||||
Get.back();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
MyDialog().getDialog('Biometric Authentication'.tr,
|
||||
'You should use Touch ID or Face ID to confirm payment'.tr,
|
||||
() {
|
||||
Get.back();
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
cancel: MyElevatedButton(
|
||||
title: 'Cancel'.tr,
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPurchaseInstructions() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.accentColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: AppColor.accentColor.withOpacity(0.3)),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
"You can purchase a budget to enable online access through the options listed below"
|
||||
.tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppStyle.title.copyWith(fontSize: 14),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPointsOptions() {
|
||||
return SizedBox(
|
||||
height: Get.height * 0.19,
|
||||
child: ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
children: [
|
||||
PointsCaptain(
|
||||
kolor: AppColor.greyColor,
|
||||
pricePoint: 10000,
|
||||
countPoint: '10000'),
|
||||
PointsCaptain(
|
||||
kolor: AppColor.bronze, pricePoint: 20000, countPoint: '21000'),
|
||||
PointsCaptain(
|
||||
kolor: AppColor.goldenBronze,
|
||||
pricePoint: 40000,
|
||||
countPoint: '45000'),
|
||||
PointsCaptain(
|
||||
kolor: AppColor.gold, pricePoint: 100000, countPoint: '110000'),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// ويدجت مُحسّن لعرض صف معلومات الرصيد
|
||||
class _BudgetInfoRow extends StatelessWidget {
|
||||
final String title;
|
||||
final String amount;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const _BudgetInfoRow({
|
||||
required this.title,
|
||||
required this.amount,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
style: AppStyle.title.copyWith(fontSize: 16),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.accentColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
child: Text(
|
||||
'$amount ${'S.P'.tr}',
|
||||
style:
|
||||
const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// ويدجت مُحسّن لعرض بطاقة العرض الترويجي
|
||||
class _PromoProgressCard extends StatelessWidget {
|
||||
final String title;
|
||||
final String timePromo;
|
||||
final int count;
|
||||
final int maxCount;
|
||||
final String description;
|
||||
final CaptainWalletController controller;
|
||||
|
||||
const _PromoProgressCard({
|
||||
required this.title,
|
||||
required this.timePromo,
|
||||
required this.count,
|
||||
required this.maxCount,
|
||||
required this.description,
|
||||
required this.controller,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
elevation: 2,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
MyDialog().getDialog(title, description, () async {
|
||||
if (count >= maxCount) {
|
||||
controller.addDriverWalletFromPromo(timePromo, 50);
|
||||
}
|
||||
Get.back();
|
||||
});
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(title,
|
||||
style:
|
||||
AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
|
||||
Text(
|
||||
'$count / $maxCount',
|
||||
style: AppStyle.title.copyWith(color: AppColor.blueColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: LinearProgressIndicator(
|
||||
minHeight: 12,
|
||||
value: count / maxCount,
|
||||
backgroundColor: AppColor.accentColor.withOpacity(0.2),
|
||||
color: count >= maxCount
|
||||
? AppColor.greenColor
|
||||
: AppColor.blueColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// --- الدوال والويدجتس الخاصة بالدفع في سوريا تبقى كما هي ---
|
||||
// This function is a placeholder for adding Syrian payment methods.
|
||||
// You would implement the UI and logic for mobile wallets or other local options here.
|
||||
Future<dynamic> addSyrianPaymentMethod(
|
||||
CaptainWalletController captainWalletController) {
|
||||
return Get.defaultDialog(
|
||||
title: "Insert Payment Details".tr,
|
||||
content: Form(
|
||||
key: captainWalletController.formKeyAccount,
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
"Insert your mobile wallet details to receive your money weekly"
|
||||
.tr),
|
||||
MyTextForm(
|
||||
controller: captainWalletController
|
||||
.cardBank, // Re-using for mobile number
|
||||
label: "Insert mobile wallet number".tr,
|
||||
hint: '0912 345 678',
|
||||
type: TextInputType.phone),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
MyDropDownSyria() // Dropdown for Syrian providers
|
||||
],
|
||||
)),
|
||||
confirm: MyElevatedButton(
|
||||
title: 'Ok'.tr,
|
||||
onPressed: () async {
|
||||
if (captainWalletController.formKeyAccount.currentState!
|
||||
.validate()) {
|
||||
Get.back();
|
||||
// Replace with your actual API endpoint and payload for Syria
|
||||
var res =
|
||||
await CRUD().post(link: AppLink.updateAccountBank, payload: {
|
||||
"paymentProvider":
|
||||
Get.find<SyrianPayoutController>().dropdownValue.toString(),
|
||||
"accountNumber":
|
||||
captainWalletController.cardBank.text.toString(),
|
||||
"id": box.read(BoxName.driverID).toString()
|
||||
});
|
||||
print('res: $res');
|
||||
if (res != 'failure') {
|
||||
mySnackbarSuccess('Payment details added successfully'.tr);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// A new GetX controller for the Syrian payout dropdown
|
||||
class SyrianPayoutController extends GetxController {
|
||||
String dropdownValue = 'syriatel';
|
||||
|
||||
void changeValue(String? newValue) {
|
||||
if (newValue != null) {
|
||||
dropdownValue = newValue;
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A new Dropdown widget for Syrian mobile wallet providers
|
||||
class MyDropDownSyria extends StatelessWidget {
|
||||
const MyDropDownSyria({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(SyrianPayoutController());
|
||||
return GetBuilder<SyrianPayoutController>(builder: (controller) {
|
||||
return DropdownButton<String>(
|
||||
value: controller.dropdownValue,
|
||||
icon: const Icon(Icons.arrow_drop_down),
|
||||
elevation: 16,
|
||||
isExpanded: true,
|
||||
style: const TextStyle(color: Colors.deepPurple),
|
||||
underline: Container(
|
||||
height: 2,
|
||||
color: Colors.deepPurpleAccent,
|
||||
),
|
||||
onChanged: (String? newValue) {
|
||||
controller.changeValue(newValue);
|
||||
},
|
||||
items: <String>['syriatel', 'mtn']
|
||||
.map<DropdownMenuItem<String>>((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value.tr),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
141
lib/views/home/my_wallet/weekly_payment_page.dart
Executable file
141
lib/views/home/my_wallet/weekly_payment_page.dart
Executable file
@@ -0,0 +1,141 @@
|
||||
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/views/widgets/my_scafold.dart';
|
||||
import 'package:sefer_driver/views/widgets/mycircular.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../../controller/payment/driver_payment_controller.dart';
|
||||
|
||||
class WeeklyPaymentPage extends StatelessWidget {
|
||||
const WeeklyPaymentPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(DriverWalletHistoryController());
|
||||
return MyScafolld(
|
||||
title: 'Payment History'.tr,
|
||||
body: [
|
||||
GetBuilder<DriverWalletHistoryController>(
|
||||
builder: (controller) => controller.isLoading
|
||||
? const MyCircularProgressIndicator()
|
||||
: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: Get.width * .8,
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Container(
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
controller.weeklyList.isEmpty
|
||||
? '0'
|
||||
: controller.weeklyList[0]
|
||||
['totalAmount']
|
||||
.toString(),
|
||||
style: AppStyle.number,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
' Total weekly is '.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10, vertical: 5),
|
||||
child: SizedBox(
|
||||
height: Get.height * .75,
|
||||
child: controller.weeklyList.isNotEmpty
|
||||
? ListView.builder(
|
||||
itemCount: controller.weeklyList.length,
|
||||
itemBuilder:
|
||||
(BuildContext context, int index) {
|
||||
var list = controller.weeklyList[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: Container(
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Column(
|
||||
children: [
|
||||
Card(
|
||||
elevation: 2,
|
||||
color: list['paymentMethod'] ==
|
||||
'visa'
|
||||
? AppColor.blueColor
|
||||
: AppColor.secondaryColor,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
list['paymentMethod'] ==
|
||||
'Remainder'
|
||||
? 'Remainder'.tr
|
||||
: list['paymentMethod'] ==
|
||||
'fromBudget'
|
||||
? 'fromBudget'.tr
|
||||
: list[
|
||||
'paymentMethod'],
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Card(
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.all(
|
||||
8.0),
|
||||
child: Text(
|
||||
list['amount'],
|
||||
style: AppStyle.number,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
DateFormat(
|
||||
'yyyy-MM-dd hh:mm a')
|
||||
.format(DateTime.parse(
|
||||
list[
|
||||
'dateUpdated'])),
|
||||
style: AppStyle.number,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
: const SizedBox(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
isleading: true);
|
||||
}
|
||||
}
|
||||
33
lib/views/home/on_boarding_page.dart
Executable file
33
lib/views/home/on_boarding_page.dart
Executable file
@@ -0,0 +1,33 @@
|
||||
import 'package:sefer_driver/constant/colors.dart';
|
||||
import 'package:sefer_driver/controller/auth/onboarding_controller.dart';
|
||||
import 'package:sefer_driver/onbording_page.dart';
|
||||
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class OnBoardingPage extends StatelessWidget {
|
||||
OnBoardingControllerImp onBoardingControllerImp =
|
||||
Get.put(OnBoardingControllerImp());
|
||||
|
||||
OnBoardingPage({super.key});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColor.secondaryColor,
|
||||
body: SafeArea(
|
||||
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||
SizedBox(
|
||||
height: Get.height * .7,
|
||||
child: const CustomSliderOnBoarding(),
|
||||
),
|
||||
const CustomDotControllerOnBoarding(),
|
||||
// const Spacer(flex: 2),
|
||||
const SizedBox(height: 20),
|
||||
MyElevatedButton(
|
||||
onPressed: () => onBoardingControllerImp.next(),
|
||||
title: 'Next'.tr,
|
||||
)
|
||||
]),
|
||||
));
|
||||
}
|
||||
}
|
||||
89
lib/views/home/profile/behavior_page.dart
Normal file
89
lib/views/home/profile/behavior_page.dart
Normal file
@@ -0,0 +1,89 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../controller/home/captin/behavior_controller.dart';
|
||||
|
||||
class BehaviorPage extends StatelessWidget {
|
||||
const BehaviorPage({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final controller = Get.put(DriverBehaviorController());
|
||||
controller.fetchDriverBehavior();
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Driver Behavior'.tr),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: Obx(() {
|
||||
if (controller.isLoading.value) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Card(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12)),
|
||||
elevation: 4,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
children: [
|
||||
const Text("Overall Behavior Score",
|
||||
style: TextStyle(
|
||||
fontSize: 20, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"${controller.overallScore.value.toStringAsFixed(1)} / 100",
|
||||
style: const TextStyle(
|
||||
fontSize: 28, color: Colors.blue)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const Text("Last 10 Trips",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 10),
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: controller.lastTrips.length,
|
||||
itemBuilder: (context, index) {
|
||||
var trip = controller.lastTrips[index];
|
||||
return Card(
|
||||
elevation: 3,
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: Colors.blue,
|
||||
child: Text("${index + 1}",
|
||||
style: const TextStyle(color: Colors.white)),
|
||||
),
|
||||
title: Text("Trip ID: ${trip['trip_id']}"),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Behavior Score: ${trip['behavior_score']}"),
|
||||
Text("Max Speed: ${trip['max_speed']} km/h"),
|
||||
Text("Hard Brakes: ${trip['hard_brakes']}"),
|
||||
Text("Distance: ${trip['total_distance']} km"),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
130
lib/views/home/profile/captains_cars.dart
Executable file
130
lib/views/home/profile/captains_cars.dart
Executable file
@@ -0,0 +1,130 @@
|
||||
import 'package:sefer_driver/constant/box_name.dart';
|
||||
import 'package:sefer_driver/constant/colors.dart';
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
import 'package:sefer_driver/controller/functions/encrypt_decrypt.dart';
|
||||
import 'package:sefer_driver/main.dart';
|
||||
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
||||
import 'package:sefer_driver/views/widgets/mycircular.dart';
|
||||
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../auth/captin/driver_car_controller.dart';
|
||||
import '../../widgets/elevated_btn.dart';
|
||||
import 'cars_inserting_page.dart';
|
||||
|
||||
class CaptainsCars extends StatelessWidget {
|
||||
const CaptainsCars({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(DriverCarController());
|
||||
return MyScafolld(
|
||||
title: "Add new car".tr,
|
||||
body: [
|
||||
Column(
|
||||
children: [
|
||||
MyElevatedButton(
|
||||
title: "Add new car".tr,
|
||||
onPressed: () async {
|
||||
Get.to(() => CarsInsertingPage());
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: GetBuilder<DriverCarController>(
|
||||
builder: (controller) {
|
||||
return controller.isLoading
|
||||
? const MyCircularProgressIndicator()
|
||||
: ListView.builder(
|
||||
itemCount: controller.cars.length,
|
||||
itemBuilder: (context, index) {
|
||||
final car = controller.cars[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: Card(
|
||||
color: car['isDefault'].toString() == '0'
|
||||
? AppColor.accentColor
|
||||
: AppColor.blueColor,
|
||||
elevation: 2,
|
||||
child: ListTile(
|
||||
leading: Icon(
|
||||
Fontisto.car,
|
||||
size: 50,
|
||||
color: Color(int.parse(car['color_hex']
|
||||
.replaceFirst('#', '0xff'))),
|
||||
),
|
||||
title: Text(
|
||||
car['make'],
|
||||
style: AppStyle.title,
|
||||
), // Assuming `make` is a field in each car item
|
||||
subtitle: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
car['model'],
|
||||
style: AppStyle.title,
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: AppColor.blueColor)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4),
|
||||
child: Text(
|
||||
(car['car_plate']),
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
car['year'],
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
), // Assuming `model` is a field in each car item
|
||||
// trailing: IconButton(
|
||||
// icon: const Icon(
|
||||
// Icons.delete,
|
||||
// color: AppColor.redColor,
|
||||
// ),
|
||||
// onPressed: () {
|
||||
// // Add logic here to remove a car
|
||||
// MyDialog()
|
||||
// .getDialog('Are you sure to delete this car', '', () {
|
||||
// controller
|
||||
// .removeCar(car['id'].toString());
|
||||
// });
|
||||
|
||||
// },
|
||||
// ),
|
||||
onTap: () {
|
||||
MyDialog().getDialog(
|
||||
'Are you sure to make this car as default'
|
||||
.tr,
|
||||
'', () {
|
||||
Get.back();
|
||||
//make it default
|
||||
controller.updateCarRegistration(
|
||||
car['id'].toString(),
|
||||
box.read(BoxName.driverID).toString(),
|
||||
);
|
||||
});
|
||||
// Add logic to view or edit the car details
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
isleading: true);
|
||||
}
|
||||
}
|
||||
300
lib/views/home/profile/cars_inserting_page.dart
Executable file
300
lib/views/home/profile/cars_inserting_page.dart
Executable file
@@ -0,0 +1,300 @@
|
||||
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
|
||||
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.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 '../../auth/captin/driver_car_controller.dart';
|
||||
|
||||
class CarsInsertingPage extends StatelessWidget {
|
||||
CarsInsertingPage({super.key});
|
||||
final driverCarController = Get.put(DriverCarController());
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(AI());
|
||||
return MyScafolld(
|
||||
title: 'Insert New Car'.tr,
|
||||
body: [
|
||||
Container(
|
||||
color: AppColor.accentColor.withOpacity(.2),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ListView(
|
||||
// crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
egyptCarLicenceFront(),
|
||||
egyptCarLicenceBack(),
|
||||
const SizedBox(height: 10),
|
||||
Text('Please make sure to read the license carefully.'.tr),
|
||||
Text(
|
||||
'If your car license has the new design, upload the front side with two images.'
|
||||
.tr),
|
||||
const SizedBox(height: 10),
|
||||
MyElevatedButton(
|
||||
title: 'Please upload this license.'.tr,
|
||||
onPressed: () {
|
||||
final aiFront =
|
||||
Get.find<AI>().responseIdCardDriverEgyptFront;
|
||||
final aiBack =
|
||||
Get.find<AI>().responseIdCardDriverEgyptBack;
|
||||
driverCarController.addCarsForDrivers(
|
||||
aiBack['chassis'].toString(),
|
||||
aiBack['car_plate'].toString(),
|
||||
aiBack['make'].toString(),
|
||||
aiBack['model'].toString(),
|
||||
aiBack['year'].toString(),
|
||||
aiFront['LicenseExpirationDate'].toString(),
|
||||
aiBack['color'].toString(),
|
||||
aiBack['color_hex'].toString(),
|
||||
aiFront['address'].toString(),
|
||||
aiFront['owner'].toString(),
|
||||
aiBack['inspection_date'].toString(),
|
||||
aiBack['engine'].toString(),
|
||||
aiBack['fuel'].toString(),
|
||||
);
|
||||
})
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
isleading: true);
|
||||
}
|
||||
}
|
||||
|
||||
GetBuilder<AI> egyptCarLicenceFront() {
|
||||
return GetBuilder<AI>(
|
||||
builder: (ai) {
|
||||
if (ai.responseIdCardDriverEgyptFront.isNotEmpty) {
|
||||
// No need to access ai.responseIdCardDriverEgyptBack anymore
|
||||
final licenseExpiryDate = DateTime.parse(
|
||||
ai.responseIdCardDriverEgyptFront['LicenseExpirationDate']);
|
||||
|
||||
// Check if license has expired
|
||||
final today = DateTime.now();
|
||||
final isLicenseExpired = licenseExpiryDate.isBefore(today);
|
||||
|
||||
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(
|
||||
children: [
|
||||
Text('Vehicle Details Front'.tr,
|
||||
style: AppStyle.headTitle2),
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
ai.allMethodForAI((ai.prompts[3]['prompt'].toString()),
|
||||
AppLink.uploadEgypt, 'car_front');
|
||||
},
|
||||
icon: const Icon(Icons.refresh),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
const Divider(color: AppColor.accentColor),
|
||||
const SizedBox(height: 8.0),
|
||||
// Removed Make, Model, etc. as they are not available
|
||||
|
||||
Text(
|
||||
'${'Plate Number'.tr}: ${ai.responseIdCardDriverEgyptFront['car_plate']}',
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
Text(
|
||||
'${'Owner Name'.tr}: ${ai.responseIdCardDriverEgyptFront['owner']}',
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
Text(
|
||||
'${'Address'.tr}: ${ai.responseIdCardDriverEgyptFront['address']}',
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'${'License Expiry Date'.tr}: ${licenseExpiryDate.toString().substring(0, 10)}',
|
||||
style: TextStyle(
|
||||
color: isLicenseExpired ? Colors.red : Colors.green,
|
||||
),
|
||||
),
|
||||
// Removed Fuel as it's not available
|
||||
],
|
||||
),
|
||||
// Removed Inspection Date as it's not available
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Card(
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
ai.allMethodForAINewCar((ai.prompts[3]['prompt'].toString()),
|
||||
AppLink.uploadEgypt1, 'car_front', 'carId'); //todo
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/images/3.png',
|
||||
height: Get.height * .25,
|
||||
width: double.maxFinite,
|
||||
fit: BoxFit.fitHeight,
|
||||
),
|
||||
Text(
|
||||
'Capture an Image of Your car license front '.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
GetBuilder<AI> egyptCarLicenceBack() {
|
||||
return GetBuilder<AI>(
|
||||
builder: (ai) {
|
||||
if (ai.responseIdCardDriverEgyptBack.isNotEmpty) {
|
||||
// Get the tax expiry date from the response
|
||||
final taxExpiryDate =
|
||||
ai.responseIdCardDriverEgyptBack['tax_expiry'].toString();
|
||||
// final displacement = ai.responseIdCardDriverEgyptBack['displacement'];
|
||||
// if (int.parse(displacement) < 1000) {}
|
||||
// Get the inspection date from the response
|
||||
final inspectionDate =
|
||||
ai.responseIdCardDriverEgyptBack['inspection_date'].toString();
|
||||
final year = int.parse(inspectionDate.toString().split('-')[0]);
|
||||
|
||||
// Set inspectionDateTime to December 31st of the given year
|
||||
final inspectionDateTime = DateTime(year, 12, 31);
|
||||
String carBackLicenseExpired =
|
||||
inspectionDateTime.toString().split(' ')[0];
|
||||
// Get the current date
|
||||
final today = DateTime.now();
|
||||
|
||||
// Try parsing the tax expiry date. If it fails, set it to null.
|
||||
final taxExpiryDateTime = DateTime.tryParse(taxExpiryDate ?? '');
|
||||
final isExpired =
|
||||
taxExpiryDateTime != null && taxExpiryDateTime.isBefore(today);
|
||||
// Check if the inspection date is before today
|
||||
bool isInspectionExpired = inspectionDateTime.isBefore(today);
|
||||
|
||||
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('Vehicle Details Back'.tr, style: AppStyle.headTitle2),
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
ai.allMethodForAI((ai.prompts[4]['prompt'].toString()),
|
||||
AppLink.uploadEgypt, 'car_back');
|
||||
},
|
||||
icon: const Icon(Icons.refresh),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
const Divider(color: AppColor.accentColor),
|
||||
const SizedBox(height: 8.0),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'${'Make'.tr}: ${ai.responseIdCardDriverEgyptBack['make']}'),
|
||||
Text(
|
||||
'${'Model'.tr}: ${ai.responseIdCardDriverEgyptBack['model']}'),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'${'Year'.tr}: ${ai.responseIdCardDriverEgyptBack['year']}'),
|
||||
Text(
|
||||
'${'Chassis'.tr}: ${ai.responseIdCardDriverEgyptBack['chassis']}'),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'${'Color'.tr}: ${ai.responseIdCardDriverEgyptBack['color']}'),
|
||||
Text(
|
||||
'${'Displacement'.tr}: ${ai.responseIdCardDriverEgyptBack['displacement']} cc'),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
Text(
|
||||
'${'Fuel'.tr}: ${ai.responseIdCardDriverEgyptBack['fuel']}'),
|
||||
const SizedBox(height: 8.0),
|
||||
if (taxExpiryDateTime != null)
|
||||
Text(
|
||||
'${'Tax Expiry Date'.tr}: $taxExpiryDate',
|
||||
style: TextStyle(
|
||||
color: isExpired ? Colors.red : Colors.green,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'${'Inspection Date'.tr}: $carBackLicenseExpired',
|
||||
style: TextStyle(
|
||||
color: isInspectionExpired ? Colors.red : Colors.green,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Card(
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
ai.allMethodForAI((ai.prompts[4]['prompt'].toString()),
|
||||
AppLink.uploadEgypt, 'car_back');
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/images/4.png',
|
||||
height: Get.height * .25,
|
||||
width: double.maxFinite,
|
||||
fit: BoxFit.fitHeight,
|
||||
),
|
||||
Text(
|
||||
'Capture an Image of Your car license back'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
60
lib/views/home/profile/feed_back_page.dart
Executable file
60
lib/views/home/profile/feed_back_page.dart
Executable file
@@ -0,0 +1,60 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/controller/home/profile/feed_back_controller.dart';
|
||||
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
||||
import 'package:sefer_driver/views/widgets/mycircular.dart';
|
||||
|
||||
import '../../widgets/elevated_btn.dart';
|
||||
|
||||
class FeedBackPage extends StatelessWidget {
|
||||
FeedBackPage({super.key});
|
||||
FeedBackController feedBackController = Get.put(FeedBackController());
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MyScafolld(
|
||||
title: 'Feed Back'.tr,
|
||||
body: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(26),
|
||||
child: Form(
|
||||
key: feedBackController.formKey,
|
||||
child: Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: feedBackController.feedbackController,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
hintText: 'Enter your feedback here'.tr,
|
||||
labelText: 'Feedback',
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Please enter your feedback.';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
feedBackController.isLoading
|
||||
? const MyCircularProgressIndicator()
|
||||
: MyElevatedButton(
|
||||
onPressed: () {
|
||||
if (feedBackController.formKey.currentState!
|
||||
.validate()) {
|
||||
feedBackController.addFeedBack();
|
||||
|
||||
// Clear the feedback form
|
||||
feedBackController.formKey.currentState!.reset();
|
||||
}
|
||||
},
|
||||
title: 'Submit '.tr,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
isleading: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
303
lib/views/home/profile/passenger_profile_page.dart
Executable file
303
lib/views/home/profile/passenger_profile_page.dart
Executable file
@@ -0,0 +1,303 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/constant/box_name.dart';
|
||||
import 'package:sefer_driver/constant/colors.dart';
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
import 'package:sefer_driver/controller/profile/profile_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/my_textField.dart';
|
||||
import 'package:sefer_driver/views/widgets/mycircular.dart';
|
||||
|
||||
import '../../../controller/functions/log_out.dart';
|
||||
|
||||
class PassengerProfilePage extends StatelessWidget {
|
||||
PassengerProfilePage({super.key});
|
||||
LogOutController logOutController = Get.put(LogOutController());
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(ProfileController());
|
||||
|
||||
return MyScafolld(
|
||||
isleading: true,
|
||||
title: 'My Profile'.tr,
|
||||
body: [
|
||||
GetBuilder<ProfileController>(
|
||||
builder: (controller) => controller.isloading
|
||||
? const MyCircularProgressIndicator()
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: SizedBox(
|
||||
height: Get.height,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Edit Profile'.tr,
|
||||
style: AppStyle.headTitle2,
|
||||
),
|
||||
ListTile(
|
||||
title: Text(
|
||||
'Name'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
leading: const Icon(
|
||||
Icons.person_pin_rounded,
|
||||
size: 35,
|
||||
),
|
||||
trailing: const Icon(Icons.arrow_forward_ios),
|
||||
subtitle: Text(
|
||||
'${controller.prfoileData['first_name']} ${controller.prfoileData['last_name']}'),
|
||||
onTap: () {
|
||||
controller.updatField(
|
||||
'first_name', TextInputType.name);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text(
|
||||
'Gender'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
leading: Image.asset(
|
||||
'assets/images/gender.png',
|
||||
width: 35,
|
||||
),
|
||||
trailing: const Icon(Icons.arrow_forward_ios),
|
||||
subtitle: Text(
|
||||
controller.prfoileData['gender'].toString()),
|
||||
onTap: () {
|
||||
Get.defaultDialog(
|
||||
title: 'Update Gender'.tr,
|
||||
content: Column(
|
||||
children: [
|
||||
GenderPicker(),
|
||||
MyElevatedButton(
|
||||
title: 'Update'.tr,
|
||||
onPressed: () {
|
||||
controller.updateColumn({
|
||||
'id': controller.prfoileData['id']
|
||||
.toString(),
|
||||
'gender': controller.gender,
|
||||
});
|
||||
Get.back();
|
||||
},
|
||||
)
|
||||
],
|
||||
));
|
||||
// controller.updatField('gender');
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text(
|
||||
'Education'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
leading: Image.asset(
|
||||
'assets/images/education.png',
|
||||
width: 35,
|
||||
),
|
||||
trailing: const Icon(Icons.arrow_forward_ios),
|
||||
subtitle: Text(controller.prfoileData['education']
|
||||
.toString()),
|
||||
onTap: () {
|
||||
Get.defaultDialog(
|
||||
barrierDismissible: true,
|
||||
title: 'Update Education'.tr,
|
||||
content: SizedBox(
|
||||
height: 200,
|
||||
child: Column(
|
||||
children: [
|
||||
EducationDegreePicker(),
|
||||
],
|
||||
),
|
||||
),
|
||||
confirm: MyElevatedButton(
|
||||
title: 'Update Education'.tr,
|
||||
onPressed: () {
|
||||
controller.updateColumn({
|
||||
'id': controller.prfoileData['id']
|
||||
.toString(),
|
||||
'education':
|
||||
controller.selectedDegree,
|
||||
});
|
||||
Get.back();
|
||||
},
|
||||
));
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text(
|
||||
'Employment Type'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
leading: Image.asset(
|
||||
'assets/images/employmentType.png',
|
||||
width: 35,
|
||||
),
|
||||
trailing: const Icon(Icons.arrow_forward_ios),
|
||||
subtitle: Text(controller
|
||||
.prfoileData['employmentType']
|
||||
.toString()),
|
||||
onTap: () {
|
||||
controller.updatField(
|
||||
'employmentType', TextInputType.name);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text(
|
||||
'Marital Status'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
leading: Image.asset(
|
||||
'assets/images/maritalStatus.png',
|
||||
width: 35,
|
||||
),
|
||||
trailing: const Icon(Icons.arrow_forward_ios),
|
||||
subtitle: Text(controller
|
||||
.prfoileData['maritalStatus']
|
||||
.toString()),
|
||||
onTap: () {
|
||||
controller.updatField(
|
||||
'maritalStatus', TextInputType.name);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text(
|
||||
'SOS Phone'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
leading: const Icon(
|
||||
Icons.sos,
|
||||
color: AppColor.redColor,
|
||||
size: 35,
|
||||
),
|
||||
trailing: const Icon(Icons.arrow_forward_ios),
|
||||
subtitle: Text(controller.prfoileData['sosPhone']
|
||||
.toString()),
|
||||
onTap: () async {
|
||||
await controller.updatField(
|
||||
'sosPhone', TextInputType.phone);
|
||||
box.write(BoxName.sosPhonePassenger,
|
||||
controller.prfoileData['sosPhone']);
|
||||
},
|
||||
),
|
||||
// const Spacer(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
MyElevatedButton(
|
||||
title: 'Sign Out'.tr,
|
||||
onPressed: () {
|
||||
LogOutController().logOutPassenger();
|
||||
}),
|
||||
GetBuilder<LogOutController>(
|
||||
builder: (logOutController) {
|
||||
return MyElevatedButton(
|
||||
title: 'Delete My Account'.tr,
|
||||
onPressed: () {
|
||||
Get.defaultDialog(
|
||||
title:
|
||||
'Are you sure to delete your account?'
|
||||
.tr,
|
||||
content: Form(
|
||||
key: logOutController.formKey1,
|
||||
child: MyTextForm(
|
||||
controller: logOutController
|
||||
.emailTextController,
|
||||
label: 'Type your Email'.tr,
|
||||
hint: 'Type your Email'.tr,
|
||||
type:
|
||||
TextInputType.emailAddress,
|
||||
),
|
||||
),
|
||||
confirm: MyElevatedButton(
|
||||
title: 'Delete My Account'.tr,
|
||||
kolor: AppColor.redColor,
|
||||
onPressed: () async {
|
||||
await logOutController
|
||||
.deletePassengerAccount();
|
||||
}),
|
||||
cancel: MyElevatedButton(
|
||||
title: 'No I want'.tr,
|
||||
onPressed: () {
|
||||
logOutController
|
||||
.emailTextController
|
||||
.clear();
|
||||
logOutController.update();
|
||||
Get.back();
|
||||
}));
|
||||
});
|
||||
}),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GenderPicker extends StatelessWidget {
|
||||
final ProfileController controller = Get.put(ProfileController());
|
||||
|
||||
final List<String> genderOptions = ['Male'.tr, 'Female'.tr, 'Other'.tr];
|
||||
|
||||
GenderPicker({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 100,
|
||||
child: CupertinoPicker(
|
||||
itemExtent: 32.0,
|
||||
onSelectedItemChanged: (int index) {
|
||||
controller.setGender(genderOptions[index]);
|
||||
},
|
||||
children: genderOptions.map((String value) {
|
||||
return Text(value);
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EducationDegreePicker extends StatelessWidget {
|
||||
final ProfileController controller = Get.put(ProfileController());
|
||||
|
||||
final List<String> degreeOptions = [
|
||||
'High School Diploma'.tr,
|
||||
'Associate Degree'.tr,
|
||||
'Bachelor\'s Degree'.tr,
|
||||
'Master\'s Degree'.tr,
|
||||
'Doctoral Degree'.tr,
|
||||
];
|
||||
|
||||
EducationDegreePicker({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 200,
|
||||
child: CupertinoPicker(
|
||||
// backgroundColor: AppColor.accentColor,
|
||||
// looping: true,
|
||||
squeeze: 2,
|
||||
// diameterRatio: 5,
|
||||
itemExtent: 32,
|
||||
onSelectedItemChanged: (int index) {
|
||||
controller.setDegree(degreeOptions[index]);
|
||||
},
|
||||
children: degreeOptions.map((String value) {
|
||||
return Text(value);
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
377
lib/views/home/profile/profile_captain.dart
Executable file
377
lib/views/home/profile/profile_captain.dart
Executable file
@@ -0,0 +1,377 @@
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
import 'package:sefer_driver/controller/functions/encrypt_decrypt.dart';
|
||||
import 'package:sefer_driver/controller/home/payment/captain_wallet_controller.dart';
|
||||
import 'package:sefer_driver/views/auth/captin/criminal_documents_page.dart';
|
||||
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
|
||||
import 'package:sefer_driver/views/widgets/mycircular.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/controller/profile/captain_profile_controller.dart';
|
||||
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
||||
|
||||
import '../my_wallet/walet_captain.dart';
|
||||
import 'behavior_page.dart';
|
||||
import 'captains_cars.dart';
|
||||
|
||||
class ProfileCaptain extends StatelessWidget {
|
||||
ProfileCaptain({super.key});
|
||||
CaptainWalletController captainWalletController = CaptainWalletController();
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(CaptainProfileController());
|
||||
return MyScafolld(
|
||||
title: 'My Profile'.tr,
|
||||
body: [
|
||||
GetBuilder<CaptainProfileController>(
|
||||
builder: (controller) => Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: SingleChildScrollView(
|
||||
child: Center(
|
||||
child: controller.isLoading
|
||||
? const MyCircularProgressIndicator()
|
||||
: Column(
|
||||
children: [
|
||||
Material(
|
||||
elevation: 2,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
// addBankCodeEgypt(captainWalletController);
|
||||
},
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 16, horizontal: 16),
|
||||
child: Text(
|
||||
'Add bank Account'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
Get.to(() => BehaviorPage());
|
||||
},
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 16, horizontal: 16),
|
||||
child: Text(
|
||||
'Show behavior page'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
MyElevatedButton(
|
||||
title: 'Show my Cars'.tr,
|
||||
onPressed: () async {
|
||||
Get.to(() => CaptainsCars());
|
||||
},
|
||||
),
|
||||
// const SizedBox(height: 15),
|
||||
MyElevatedButton(
|
||||
title: 'Add criminal page'.tr,
|
||||
onPressed: () async {
|
||||
Get.to(() => CriminalDocumemtPage());
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: Get.height * .7,
|
||||
child: DriverProfileCard(
|
||||
driverId:
|
||||
controller.captainProfileData['driverID'] ??
|
||||
'',
|
||||
name:
|
||||
'${((controller.captainProfileData['first_name']) ?? '')} ${((controller.captainProfileData['last_name']) ?? '')}',
|
||||
phoneNumber:
|
||||
controller.captainProfileData['phone'] ?? '',
|
||||
email:
|
||||
controller.captainProfileData['email'] ?? '',
|
||||
birthdate: controller
|
||||
.captainProfileData['birthdate'] is String
|
||||
? controller.captainProfileData['birthdate']
|
||||
: '',
|
||||
gender: controller.captainProfileData['gender']
|
||||
is String
|
||||
? controller.captainProfileData['gender']
|
||||
: '',
|
||||
education: controller
|
||||
.captainProfileData['education'] is String
|
||||
? controller.captainProfileData['education']
|
||||
: '',
|
||||
carMake:
|
||||
controller.captainProfileData['make'] ?? '',
|
||||
carModel:
|
||||
controller.captainProfileData['model'] ?? '',
|
||||
carPlate:
|
||||
controller.captainProfileData['car_plate'] ??
|
||||
'',
|
||||
carColor:
|
||||
controller.captainProfileData['color'] ?? '',
|
||||
vin: controller.captainProfileData['vin'] ?? '',
|
||||
registrationDate: controller.captainProfileData[
|
||||
'registration_date'] ??
|
||||
'',
|
||||
expirationDate: controller
|
||||
.captainProfileData['expiration_date'] ??
|
||||
'',
|
||||
ratingCount: int.tryParse(controller
|
||||
.captainProfileData['ratingCount']
|
||||
.toString()) ??
|
||||
0,
|
||||
ratingDriver: controller
|
||||
.captainProfileData['ratingDriver'] !=
|
||||
null
|
||||
? double.tryParse(controller
|
||||
.captainProfileData['ratingDriver']
|
||||
.toString()) ??
|
||||
0
|
||||
: null,
|
||||
age: int.tryParse(controller
|
||||
.captainProfileData['age']
|
||||
.toString()) ??
|
||||
0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
isleading: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DriverProfileCard extends StatelessWidget {
|
||||
final String driverId;
|
||||
final String name;
|
||||
final String phoneNumber;
|
||||
final String email;
|
||||
final String birthdate;
|
||||
final String gender;
|
||||
final String education;
|
||||
final String carMake;
|
||||
final String carModel;
|
||||
final String carPlate;
|
||||
final String carColor;
|
||||
final String vin;
|
||||
final String registrationDate;
|
||||
final String expirationDate;
|
||||
final int ratingCount;
|
||||
final double? ratingDriver;
|
||||
final int age;
|
||||
|
||||
DriverProfileCard({
|
||||
required this.driverId,
|
||||
required this.name,
|
||||
required this.phoneNumber,
|
||||
required this.email,
|
||||
required this.birthdate,
|
||||
required this.gender,
|
||||
required this.education,
|
||||
required this.carMake,
|
||||
required this.carModel,
|
||||
required this.carPlate,
|
||||
required this.carColor,
|
||||
required this.vin,
|
||||
required this.registrationDate,
|
||||
required this.expirationDate,
|
||||
required this.ratingCount,
|
||||
required this.ratingDriver,
|
||||
required this.age,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
// elevation: 8,
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
margin: const EdgeInsets.all(16),
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
style: AppStyle.title,
|
||||
name,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.phone),
|
||||
const SizedBox(width: 8),
|
||||
Text(style: AppStyle.title, phoneNumber),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.calendar_today),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
style: AppStyle.title,
|
||||
'${'birthdate'.tr} : $birthdate',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.wc),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
style: AppStyle.title,
|
||||
'${'gender'.tr} : $gender',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.school),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
style: AppStyle.title,
|
||||
'${'education'.tr} : $education',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.car_repair),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
style: AppStyle.title,
|
||||
'${'Make'.tr} : $carMake',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.model_training),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
style: AppStyle.title,
|
||||
'${'car_model'.tr} : $carModel',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.drive_eta),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
style: AppStyle.title,
|
||||
'${'car_plate'.tr} : $carPlate',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.color_lens),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
style: AppStyle.title,
|
||||
'${'car_color'.tr} : $carColor',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.confirmation_number),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
style: AppStyle.title,
|
||||
'${'vin'.tr} : $vin',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.calendar_today),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
style: AppStyle.title,
|
||||
'${'registration_date'.tr} : $registrationDate',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.calendar_today),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
style: AppStyle.title,
|
||||
'${'expiration_date'.tr} : $expirationDate',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.star),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
style: AppStyle.title,
|
||||
'${'rating_count'.tr} : $ratingCount',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.star_rate),
|
||||
const SizedBox(width: 8),
|
||||
ratingDriver != null
|
||||
? Text(
|
||||
style: AppStyle.title,
|
||||
'${'rating_driver'.tr} : $ratingDriver',
|
||||
)
|
||||
: Text(
|
||||
style: AppStyle.title,
|
||||
'${'rating_driver'.tr} : 0',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.person),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
style: AppStyle.title,
|
||||
'${'age'.tr} : $age',
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
112
lib/views/home/profile/promos_passenger_page.dart
Executable file
112
lib/views/home/profile/promos_passenger_page.dart
Executable file
@@ -0,0 +1,112 @@
|
||||
import 'package:animated_text_kit/animated_text_kit.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/controller/home/profile/promos_controller.dart';
|
||||
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../constant/style.dart';
|
||||
import '../../widgets/mycircular.dart';
|
||||
|
||||
class PromosPassengerPage extends StatelessWidget {
|
||||
const PromosPassengerPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(PromosController());
|
||||
return MyScafolld(
|
||||
title: 'Promos For today'.tr,
|
||||
isleading: true,
|
||||
body: [
|
||||
GetBuilder<PromosController>(
|
||||
builder: (orderHistoryController) => orderHistoryController.isLoading
|
||||
? const MyCircularProgressIndicator()
|
||||
: ListView.builder(
|
||||
itemCount: orderHistoryController.promoList.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final rides = orderHistoryController.promoList[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||
color: AppColor.secondaryColor,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColor.accentColor,
|
||||
offset: Offset(-3, -3),
|
||||
blurRadius: 0,
|
||||
spreadRadius: 0,
|
||||
blurStyle: BlurStyle.outer),
|
||||
BoxShadow(
|
||||
color: AppColor.accentColor,
|
||||
offset: Offset(3, 3),
|
||||
blurRadius: 0,
|
||||
spreadRadius: 0,
|
||||
blurStyle: BlurStyle.outer)
|
||||
]),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
AnimatedTextKit(
|
||||
animatedTexts: [
|
||||
ScaleAnimatedText(rides['promo_code'],
|
||||
textStyle: AppStyle.title),
|
||||
WavyAnimatedText(rides['promo_code'],
|
||||
textStyle: AppStyle.title),
|
||||
FlickerAnimatedText(
|
||||
rides['promo_code'],
|
||||
textStyle: AppStyle.title),
|
||||
WavyAnimatedText(rides['promo_code'],
|
||||
textStyle: AppStyle.title),
|
||||
],
|
||||
isRepeatingAnimation: true,
|
||||
onTap: () {},
|
||||
),
|
||||
Text(
|
||||
rides['description'],
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
rides['validity_start_date'],
|
||||
style: AppStyle.title,
|
||||
),
|
||||
Text(
|
||||
rides['validity_end_date'],
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
'Copy this Promo to use it in your Ride!'.tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppStyle.headTitle2
|
||||
.copyWith(color: AppColor.accentColor),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
88
lib/views/home/profile/taarif_page.dart
Executable file
88
lib/views/home/profile/taarif_page.dart
Executable file
@@ -0,0 +1,88 @@
|
||||
import 'package:sefer_driver/constant/box_name.dart';
|
||||
import 'package:sefer_driver/main.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
||||
|
||||
class TaarifPage extends StatelessWidget {
|
||||
const TaarifPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MyScafolld(isleading: true, title: 'Tariffs'.tr, body: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: ListView(
|
||||
// mainAxisAlignment: MainAxisAlignment.start,
|
||||
// crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
children: [
|
||||
Table(
|
||||
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
|
||||
border: TableBorder.symmetric(),
|
||||
textBaseline: TextBaseline.alphabetic,
|
||||
children: [
|
||||
TableRow(
|
||||
// decoration: AppStyle.boxDecoration,
|
||||
children: [
|
||||
Text('Minimum fare'.tr, style: AppStyle.title),
|
||||
box.read(BoxName.countryCode) == 'Jordan'
|
||||
? Text('1 ${'JOD'.tr}', style: AppStyle.title)
|
||||
: Text('20 ${'LE'.tr}', style: AppStyle.title),
|
||||
],
|
||||
),
|
||||
TableRow(
|
||||
children: [
|
||||
Text('Maximum fare'.tr, style: AppStyle.title),
|
||||
box.read(BoxName.countryCode) == 'Jordan'
|
||||
? Text('200 ${'JOD'.tr}', style: AppStyle.title)
|
||||
: Text('15000 ${'LE'.tr}', style: AppStyle.title),
|
||||
],
|
||||
),
|
||||
TableRow(
|
||||
children: [
|
||||
Text('Flag-down fee'.tr, style: AppStyle.title),
|
||||
box.read(BoxName.countryCode) == 'Jordan'
|
||||
? Text('0.47 ${'JOD'.tr}', style: AppStyle.title)
|
||||
: Text('15 ${'LE'.tr}', style: AppStyle.title),
|
||||
],
|
||||
),
|
||||
TableRow(
|
||||
children: [
|
||||
box.read(BoxName.countryCode) == 'Jordan'
|
||||
? Text('0.05 ${'JOD'.tr}/min and 0.21 ${'JOD'.tr}/km',
|
||||
style: AppStyle.title)
|
||||
: Text('1 ${'LE'.tr}/min and 4 ${'LE'.tr}/km',
|
||||
style: AppStyle.title),
|
||||
Text('Including Tax'.tr, style: AppStyle.title),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text('BookingFee'.tr, style: AppStyle.headTitle2),
|
||||
const SizedBox(height: 10),
|
||||
Text('10%', style: AppStyle.title),
|
||||
const SizedBox(height: 20),
|
||||
Text('Morning'.tr, style: AppStyle.headTitle2),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'from 07:30 till 10:30 (Thursday, Friday, Saturday, Monday)'.tr,
|
||||
style: AppStyle.title),
|
||||
const SizedBox(height: 20),
|
||||
Text('Evening'.tr, style: AppStyle.headTitle2),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'from 12:00 till 15:00 (Thursday, Friday, Saturday, Monday)'.tr,
|
||||
style: AppStyle.title),
|
||||
const SizedBox(height: 20),
|
||||
Text('Night'.tr, style: AppStyle.headTitle2),
|
||||
const SizedBox(height: 10),
|
||||
Text('from 23:59 till 05:30'.tr, style: AppStyle.title),
|
||||
],
|
||||
),
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
150
lib/views/lang/languages.dart
Executable file
150
lib/views/lang/languages.dart
Executable file
@@ -0,0 +1,150 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/views/home/Captin/home_captain/home_captin.dart';
|
||||
|
||||
import '../../controller/local/local_controller.dart';
|
||||
|
||||
class Language extends StatelessWidget {
|
||||
const Language({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text('Choose Language'.tr),
|
||||
border: null,
|
||||
),
|
||||
child: Material(
|
||||
// Wrap SafeArea with Material widget
|
||||
child: SafeArea(
|
||||
child: GetBuilder<LocaleController>(
|
||||
builder: (controller) => Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildHeader(),
|
||||
const SizedBox(height: 20),
|
||||
Expanded(
|
||||
child: ListView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
children: [
|
||||
_buildLanguageButton(
|
||||
'العربية', 'ar', controller, context, '🇪🇬'),
|
||||
_buildLanguageButton('العربية (الخليج)', 'ar-gulf',
|
||||
controller, context, '🇸🇦'),
|
||||
_buildLanguageButton('العربية (المغرب)', 'ar-ma',
|
||||
controller, context, '🇲🇦'),
|
||||
_buildLanguageButton(
|
||||
'English', 'en', controller, context, '🇺🇸'),
|
||||
_buildLanguageButton(
|
||||
'Türkçe', 'tr', controller, context, '🇹🇷'),
|
||||
_buildLanguageButton(
|
||||
'Français', 'fr', controller, context, '🇫🇷'),
|
||||
_buildLanguageButton(
|
||||
'Italiano', 'it', controller, context, '🇮🇹'),
|
||||
_buildLanguageButton(
|
||||
'Deutsch', 'de', controller, context, '🇩🇪'),
|
||||
_buildLanguageButton(
|
||||
'Ελληνικά', 'el', controller, context, '🇬🇷'),
|
||||
_buildLanguageButton(
|
||||
'Español', 'es', controller, context, '🇪🇸'),
|
||||
_buildLanguageButton(
|
||||
'فارسی', 'fa', controller, context, '🇮🇷'),
|
||||
_buildLanguageButton(
|
||||
'中文', 'zh', controller, context, '🇨🇳'),
|
||||
_buildLanguageButton(
|
||||
'Русский', 'ru', controller, context, '🇷🇺'),
|
||||
_buildLanguageButton(
|
||||
'हिन्दी', 'hi', controller, context, '🇮🇳'),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 20, bottom: 10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Language Options'.tr,
|
||||
style: const TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: CupertinoColors.black, // Or your theme primary color
|
||||
),
|
||||
textAlign: TextAlign.start,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Select your preferred language for the app interface.',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: CupertinoColors.secondaryLabel,
|
||||
),
|
||||
textAlign: TextAlign.start,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLanguageButton(String title, String langCode,
|
||||
LocaleController controller, BuildContext context, String flagIcon) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: CupertinoColors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: CupertinoColors.systemGrey5.withOpacity(0.5),
|
||||
spreadRadius: 1,
|
||||
blurRadius: 3,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: ListTile(
|
||||
leading: Text(flagIcon,
|
||||
style: const TextStyle(fontSize: 28)), // Using flag icon as leading
|
||||
title: Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
trailing: const Icon(CupertinoIcons.chevron_forward,
|
||||
color: CupertinoColors.inactiveGray),
|
||||
onTap: () async {
|
||||
controller.changeLang(langCode);
|
||||
showCupertinoDialog(
|
||||
context: context,
|
||||
builder: (context) => CupertinoAlertDialog(
|
||||
title: Text('You should restart app to change language'.tr),
|
||||
actions: [
|
||||
CupertinoDialogAction(
|
||||
child: Text('Ok'.tr),
|
||||
onPressed: () {
|
||||
Get.offAll(() => HomeCaptain());
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
353
lib/views/notification/available_rides_page.dart
Executable file
353
lib/views/notification/available_rides_page.dart
Executable file
@@ -0,0 +1,353 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:sefer_driver/constant/colors.dart';
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
import 'package:sefer_driver/controller/notification/ride_available_controller.dart';
|
||||
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
||||
import 'package:sefer_driver/views/widgets/mycircular.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../constant/box_name.dart';
|
||||
import '../../constant/links.dart';
|
||||
import '../../controller/firebase/firbase_messge.dart';
|
||||
import '../../controller/functions/crud.dart';
|
||||
import '../../controller/home/captin/home_captain_controller.dart';
|
||||
import '../../main.dart';
|
||||
import '../home/Captin/driver_map_page.dart';
|
||||
import '../widgets/mydialoug.dart';
|
||||
|
||||
class AvailableRidesPage extends StatelessWidget {
|
||||
const AvailableRidesPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(RideAvailableController());
|
||||
return GetBuilder<RideAvailableController>(
|
||||
builder: (rideAvailableController) {
|
||||
// rideAvailableController.sortRidesByDistance();
|
||||
return MyScafolld(
|
||||
title: 'Available for rides'.tr,
|
||||
body: [
|
||||
rideAvailableController.isLoading
|
||||
? const MyCircularProgressIndicator()
|
||||
:
|
||||
// : ListView.builder(
|
||||
// itemCount: rideAvailableController
|
||||
// .rideAvailableMap['message']
|
||||
// .where((rideInfo) {
|
||||
// var driverType =
|
||||
// box.read(BoxName.carTypeOfDriver).toString();
|
||||
// return (driverType == 'Comfort' &&
|
||||
// ['Speed', 'Comfort']
|
||||
// .contains(rideInfo['carType'])) ||
|
||||
// (driverType == 'Speed' &&
|
||||
// rideInfo['carType'] == 'Speed') ||
|
||||
// (driverType == 'Scooter' &&
|
||||
// rideInfo['carType'] == 'Scooter') ||
|
||||
// (driverType == 'Awfar Car' &&
|
||||
// rideInfo['carType'] == 'Awfar Car') ||
|
||||
// (driverType == 'Lady' &&
|
||||
// ['Comfort', 'Speed', 'Lady']
|
||||
// .contains(rideInfo['carType']));
|
||||
// }).length,
|
||||
// itemBuilder: (context, index) {
|
||||
// var filteredRides = rideAvailableController
|
||||
// .rideAvailableMap['message']
|
||||
// .where((rideInfo) {
|
||||
// var driverType =
|
||||
// box.read(BoxName.carTypeOfDriver).toString();
|
||||
// return (driverType == 'Comfort' &&
|
||||
// ['Speed', 'Comfort']
|
||||
// .contains(rideInfo['carType'])) ||
|
||||
// (driverType == 'Speed' &&
|
||||
// rideInfo['carType'] == 'Speed') ||
|
||||
// (driverType == 'Awfar Car' &&
|
||||
// rideInfo['carType'] == 'Awfar Car') ||
|
||||
// (driverType == 'Scooter' &&
|
||||
// rideInfo['carType'] == 'Scooter') ||
|
||||
// (driverType == 'Lady' &&
|
||||
// ['Comfort', 'Speed', 'Lady']
|
||||
// .contains(rideInfo['carType']));
|
||||
// }).toList();
|
||||
|
||||
// return RideAvailableCard(
|
||||
// rideInfo: filteredRides[index],
|
||||
// );
|
||||
// },
|
||||
// )
|
||||
ListView.builder(
|
||||
itemCount: rideAvailableController
|
||||
.rideAvailableMap['message']
|
||||
.where((rideInfo) {
|
||||
var driverType =
|
||||
box.read(BoxName.carTypeOfDriver).toString();
|
||||
switch (driverType) {
|
||||
case 'Comfort':
|
||||
return ['Speed', 'Comfort']
|
||||
.contains(rideInfo['carType']);
|
||||
case 'Speed':
|
||||
case 'Scooter':
|
||||
case 'Awfar Car':
|
||||
return rideInfo['carType'] == driverType;
|
||||
case 'Lady':
|
||||
return ['Comfort', 'Speed', 'Lady']
|
||||
.contains(rideInfo['carType']);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}).length,
|
||||
itemBuilder: (context, index) {
|
||||
var filteredRides = rideAvailableController
|
||||
.rideAvailableMap['message']
|
||||
.where((rideInfo) {
|
||||
var driverType =
|
||||
box.read(BoxName.carTypeOfDriver).toString();
|
||||
switch (driverType) {
|
||||
case 'Comfort':
|
||||
return ['Speed', 'Comfort']
|
||||
.contains(rideInfo['carType']);
|
||||
case 'Speed':
|
||||
case 'Scooter':
|
||||
case 'Awfar Car':
|
||||
return rideInfo['carType'] == driverType;
|
||||
case 'Lady':
|
||||
return ['Comfort', 'Speed', 'Lady']
|
||||
.contains(rideInfo['carType']);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}).toList();
|
||||
|
||||
return RideAvailableCard(
|
||||
rideInfo: filteredRides[index],
|
||||
);
|
||||
},
|
||||
)
|
||||
// rideAvailableController.isLoading
|
||||
// ? const MyCircularProgressIndicator()
|
||||
// : ListView.builder(
|
||||
// itemCount: rideAvailableController
|
||||
// .rideAvailableMap['message'].length,
|
||||
// itemBuilder: (context, index) => RideAvailableCard(
|
||||
// rideInfo: rideAvailableController
|
||||
// .rideAvailableMap['message'][index],
|
||||
// ),
|
||||
// )
|
||||
],
|
||||
isleading: true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class RideAvailableCard extends StatelessWidget {
|
||||
final Map<String, dynamic> rideInfo;
|
||||
|
||||
const RideAvailableCard({Key? key, required this.rideInfo}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
margin: const EdgeInsets.all(8.0),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
elevation: 4,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildLocationRow('↑', rideInfo['startName'], AppColor.greenColor),
|
||||
const SizedBox(height: 8),
|
||||
_buildLocationRow('↓', rideInfo['endName'], Colors.red),
|
||||
const SizedBox(height: 16),
|
||||
_buildInfoRow(),
|
||||
const SizedBox(height: 16),
|
||||
_buildActionRow(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLocationRow(String icon, String location, Color iconColor) {
|
||||
return Row(
|
||||
children: [
|
||||
Text(
|
||||
icon,
|
||||
style: TextStyle(
|
||||
fontSize: 20, fontWeight: FontWeight.bold, color: iconColor),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
location,
|
||||
style: AppStyle.subtitle,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInfoRow() {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('${'Price:'.tr} ${rideInfo['price']} \$', style: AppStyle.title),
|
||||
Text(
|
||||
rideInfo['carType'],
|
||||
style: AppStyle.title.copyWith(color: AppColor.greenColor),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionRow() {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('📈 ${rideInfo['passengerRate']}', style: AppStyle.title),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'📍 ${rideInfo['distance']} ${'KM'.tr}',
|
||||
style: AppStyle.title.copyWith(color: AppColor.greenColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () => _acceptRide(),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColor.greenColor,
|
||||
shape:
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
child: Text('Accept'.tr),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _acceptRide() async {
|
||||
var res = await CRUD().post(link: AppLink.updateStausFromSpeed, payload: {
|
||||
'id': rideInfo['id'],
|
||||
'rideTimeStart': DateTime.now().toString(),
|
||||
'status': 'Apply',
|
||||
'driver_id': box.read(BoxName.driverID),
|
||||
});
|
||||
if (AppLink.endPoint.toString() != AppLink.seferCairoServer) {
|
||||
CRUD().post(
|
||||
link: '${AppLink.endPoint}rides/updateStausFromSpeed.php',
|
||||
payload: {
|
||||
'id': rideInfo['id'],
|
||||
'rideTimeStart': DateTime.now().toString(),
|
||||
'status': 'Apply',
|
||||
'driver_id': box.read(BoxName.driverID),
|
||||
});
|
||||
}
|
||||
|
||||
// .then((value) {
|
||||
// var json = jsonDecode(res);
|
||||
if (res != "failure") {
|
||||
List<String> bodyToPassenger = [
|
||||
box.read(BoxName.driverID).toString(),
|
||||
box.read(BoxName.nameDriver).toString(),
|
||||
box.read(BoxName.tokenDriver).toString(),
|
||||
];
|
||||
box.write(BoxName.statusDriverLocation, 'on');
|
||||
await CRUD().postFromDialogue(link: AppLink.addDriverOrder, payload: {
|
||||
'driver_id': box.read(BoxName.driverID),
|
||||
'order_id': rideInfo['id'],
|
||||
'status': 'Apply'
|
||||
});
|
||||
await CRUD().post(link: AppLink.updateRides, payload: {
|
||||
'id': rideInfo['id'],
|
||||
'DriverIsGoingToPassenger': DateTime.now().toString(),
|
||||
'status': 'Applied'
|
||||
});
|
||||
await CRUD().post(
|
||||
link: AppLink.updateWaitingRide,
|
||||
payload: {'id': rideInfo['id'], 'status': 'Applied'});
|
||||
if (AppLink.endPoint.toString() != AppLink.seferCairoServer) {
|
||||
CRUD().postFromDialogue(
|
||||
link: '${AppLink.endPoint}/driver_order/add.php',
|
||||
payload: {
|
||||
'driver_id': box.read(BoxName.driverID),
|
||||
// box.read(BoxName.driverID).toString(),
|
||||
'order_id': rideInfo['id'],
|
||||
'status': 'Apply'
|
||||
});
|
||||
CRUD().post(link: '${AppLink.endPoint}/rides/update.php', payload: {
|
||||
'id': rideInfo['id'],
|
||||
'DriverIsGoingToPassenger': DateTime.now().toString(),
|
||||
'status': 'Applied'
|
||||
});
|
||||
CRUD().post(
|
||||
link:
|
||||
"${AppLink.endPoint}/ride/notificationCaptain/updateWaitingTrip.php",
|
||||
payload: {'id': rideInfo['id'], 'status': 'Applied'});
|
||||
}
|
||||
|
||||
FirebaseMessagesController().sendNotificationToPassengerToken(
|
||||
"Accepted Ride".tr,
|
||||
'your ride is Accepted'.tr,
|
||||
// arguments['DriverList'][9].toString(),
|
||||
rideInfo['passengerToken'].toString(),
|
||||
// box.read(BoxName.tokenDriver).toString(),
|
||||
bodyToPassenger,
|
||||
'start.wav');
|
||||
Get.back();
|
||||
Get.to(() => PassengerLocationMapPage(), arguments: {
|
||||
'passengerLocation': rideInfo['start_location'].toString(),
|
||||
'passengerDestination': rideInfo['end_location'].toString(),
|
||||
'Duration': rideInfo['duration'].toString(),
|
||||
'totalCost': rideInfo['price'].toString(),
|
||||
'Distance': rideInfo['distance'].toString(),
|
||||
'name': rideInfo['first_name'].toString(),
|
||||
'phone': rideInfo['phone'].toString(),
|
||||
'email': rideInfo['email'].toString(),
|
||||
'WalletChecked': rideInfo['payment_method'].toString(),
|
||||
'tokenPassenger': rideInfo['passengerToken'].toString(),
|
||||
'direction':
|
||||
'https://www.google.com/maps/dir/${rideInfo['start_location']}/${rideInfo['end_location']}/',
|
||||
'DurationToPassenger': rideInfo['duration'].toString(),
|
||||
'rideId': rideInfo['id'].toString(),
|
||||
'passengerId': rideInfo['passenger_id'].toString(),
|
||||
'driverId': box.read(BoxName.driverID).toString(),
|
||||
'durationOfRideValue': rideInfo['duration'].toString(),
|
||||
'paymentAmount': rideInfo['price'].toString(),
|
||||
'paymentMethod': 'cash'.toString() == //todo fix payment method
|
||||
'true'
|
||||
? 'visa'
|
||||
: 'cash',
|
||||
'isHaveSteps': 'startEnd'.toString(),
|
||||
'step0': ''.toString(),
|
||||
'step1': ''.toString(),
|
||||
'step2': ''.toString(),
|
||||
'step3': ''.toString(),
|
||||
'step4': ''.toString(),
|
||||
'passengerWalletBurc': rideInfo['bruc'].toString(),
|
||||
'timeOfOrder': DateTime.now().toString(),
|
||||
'totalPassenger': rideInfo['price'].toString(),
|
||||
'carType': rideInfo['carType'].toString(),
|
||||
'kazan': Get.find<HomeCaptainController>().kazan.toString(),
|
||||
});
|
||||
} else {
|
||||
MyDialog().getDialog(
|
||||
"This ride is already taken by another driver.".tr, '', () {
|
||||
CRUD().post(
|
||||
link: AppLink.deleteAvailableRide, payload: {'id': rideInfo['id']});
|
||||
if (AppLink.endPoint.toString() != AppLink.seferCairoServer) {
|
||||
CRUD().post(
|
||||
link:
|
||||
'${AppLink.endPoint}/ride/notificationCaptain/deleteAvailableRide.php',
|
||||
payload: {'id': rideInfo['id']});
|
||||
}
|
||||
Get.back();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
87
lib/views/notification/notification_captain.dart
Executable file
87
lib/views/notification/notification_captain.dart
Executable file
@@ -0,0 +1,87 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/controller/notification/notification_captain_controller.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class NotificationCaptain extends StatelessWidget {
|
||||
const NotificationCaptain({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(NotificationCaptainController());
|
||||
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text('Notifications'.tr),
|
||||
leading: CupertinoNavigationBarBackButton(
|
||||
onPressed: () => Get.back(),
|
||||
),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: GetBuilder<NotificationCaptainController>(
|
||||
builder: (notificationCaptainController) =>
|
||||
notificationCaptainController.isLoading
|
||||
? const Center(child: CupertinoActivityIndicator())
|
||||
: ListView.builder(
|
||||
itemCount: notificationCaptainController
|
||||
.notificationData['message'].length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
if (notificationCaptainController
|
||||
.notificationData['message'] ==
|
||||
"No notification data found") {
|
||||
_showCupertinoDialog(context, 'No Notifications',
|
||||
'There are no notifications at this time.');
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
var res = notificationCaptainController
|
||||
.notificationData['message'][index];
|
||||
return CupertinoListTile(
|
||||
leading: const Icon(CupertinoIcons.bell_fill),
|
||||
title: Text(
|
||||
res['title'],
|
||||
style:
|
||||
CupertinoTheme.of(context).textTheme.textStyle,
|
||||
),
|
||||
subtitle: Text(
|
||||
res['body'],
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.tabLabelTextStyle,
|
||||
),
|
||||
onTap: () {
|
||||
_showCupertinoDialog(
|
||||
context,
|
||||
res['title'],
|
||||
res['body'],
|
||||
onConfirm: () {
|
||||
notificationCaptainController
|
||||
.updateNotification(res['id'].toString());
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showCupertinoDialog(BuildContext context, String title, String content,
|
||||
{VoidCallback? onConfirm}) {
|
||||
showCupertinoDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) => CupertinoAlertDialog(
|
||||
title: Text(title),
|
||||
content: Text(content),
|
||||
actions: <CupertinoDialogAction>[
|
||||
CupertinoDialogAction(
|
||||
child: const Text('OK'),
|
||||
onPressed: onConfirm ?? () => Navigator.of(context).pop(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
83
lib/views/notification/notification_page.dart
Executable file
83
lib/views/notification/notification_page.dart
Executable file
@@ -0,0 +1,83 @@
|
||||
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 '../../controller/notification/passenger_notification_controller.dart';
|
||||
import '../widgets/elevated_btn.dart';
|
||||
import '../widgets/my_scafold.dart';
|
||||
import '../widgets/mycircular.dart';
|
||||
|
||||
class NotificationPage extends StatelessWidget {
|
||||
const NotificationPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(PassengerNotificationController());
|
||||
return MyScafolld(
|
||||
isleading: true,
|
||||
title: 'Notifications',
|
||||
body: [
|
||||
GetBuilder<PassengerNotificationController>(
|
||||
builder: (notificationCaptainController) =>
|
||||
notificationCaptainController.isloading
|
||||
? const MyCircularProgressIndicator()
|
||||
: SafeArea(
|
||||
child: ListView.builder(
|
||||
itemCount: notificationCaptainController
|
||||
.notificationData['message'].length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
if (notificationCaptainController
|
||||
.notificationData['message'] ==
|
||||
"No notification data found") {
|
||||
Get.defaultDialog();
|
||||
}
|
||||
var res = notificationCaptainController
|
||||
.notificationData['message'][index];
|
||||
return Card(
|
||||
elevation: 4,
|
||||
color: res['isShown'] == 'true'
|
||||
? AppColor.secondaryColor.withOpacity(.5)
|
||||
: AppColor.secondaryColor.withOpacity(.9),
|
||||
child: ListTile(
|
||||
onTap: () {
|
||||
Get.defaultDialog(
|
||||
title: res['title'],
|
||||
titleStyle: AppStyle.title,
|
||||
content: SizedBox(
|
||||
width: Get.width * .8,
|
||||
// height: Get.height * .4,
|
||||
child: Text(
|
||||
res['body'],
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
confirm: MyElevatedButton(
|
||||
title: 'Ok',
|
||||
onPressed: () {
|
||||
notificationCaptainController
|
||||
.updateNotification(
|
||||
res['id'].toString());
|
||||
}));
|
||||
},
|
||||
leading: res['isShown'] == 'true'
|
||||
? const Icon(
|
||||
Icons.notifications_off_outlined)
|
||||
: const Icon(Icons.notifications_active),
|
||||
title: Text(
|
||||
res['title'],
|
||||
style: AppStyle.title,
|
||||
),
|
||||
subtitle: Text(
|
||||
res['body'],
|
||||
style: AppStyle.subtitle,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
))
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
70
lib/views/widgets/circle_container.dart
Executable file
70
lib/views/widgets/circle_container.dart
Executable file
@@ -0,0 +1,70 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/constant/colors.dart';
|
||||
|
||||
import 'mydialoug.dart';
|
||||
|
||||
class MyCircleContainer extends StatelessWidget {
|
||||
final Widget child;
|
||||
final Color backgroundColor;
|
||||
final Color borderColor;
|
||||
|
||||
MyCircleContainer({
|
||||
Key? key,
|
||||
required this.child,
|
||||
this.backgroundColor = AppColor.secondaryColor,
|
||||
this.borderColor = AppColor.accentColor,
|
||||
}) : super(key: key);
|
||||
|
||||
final controller = Get.put(CircleController());
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<CircleController>(
|
||||
builder: ((controller) => GestureDetector(
|
||||
onTap: () {
|
||||
controller.changeColor();
|
||||
MyDialog().getDialog(
|
||||
'Rejected Orders Count'.tr,
|
||||
'This is the total number of rejected orders per day after accepting the orders'
|
||||
.tr, () {
|
||||
Get.back();
|
||||
});
|
||||
},
|
||||
child: AnimatedContainer(
|
||||
onEnd: () {
|
||||
controller.onEnd();
|
||||
},
|
||||
duration: const Duration(milliseconds: 300),
|
||||
width: controller.size,
|
||||
height: controller.size,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: controller.backgroundColor,
|
||||
border: Border.all(
|
||||
color: borderColor,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Center(child: child),
|
||||
),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
class CircleController extends GetxController {
|
||||
Color backgroundColor = AppColor.secondaryColor;
|
||||
double size = 40;
|
||||
void changeColor() {
|
||||
backgroundColor = backgroundColor == AppColor.secondaryColor
|
||||
? AppColor.accentColor
|
||||
: AppColor.secondaryColor;
|
||||
size = 60;
|
||||
update();
|
||||
}
|
||||
|
||||
void onEnd() {
|
||||
size = 40;
|
||||
update();
|
||||
}
|
||||
}
|
||||
58
lib/views/widgets/elevated_btn.dart
Executable file
58
lib/views/widgets/elevated_btn.dart
Executable file
@@ -0,0 +1,58 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:vibration/vibration.dart';
|
||||
|
||||
import '../../constant/box_name.dart';
|
||||
import '../../constant/colors.dart';
|
||||
import '../../main.dart';
|
||||
|
||||
class MyElevatedButton extends StatelessWidget {
|
||||
final String title;
|
||||
final VoidCallback onPressed;
|
||||
final Color kolor;
|
||||
final int vibrateDuration;
|
||||
const MyElevatedButton({
|
||||
Key? key,
|
||||
required this.title,
|
||||
required this.onPressed,
|
||||
this.kolor = AppColor.primaryColor,
|
||||
this.vibrateDuration = 100,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool vibrate = box.read(BoxName.isvibrate) ?? true;
|
||||
return ElevatedButton(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateProperty.all(kolor),
|
||||
shadowColor: WidgetStateProperty.all(Colors.transparent),
|
||||
shape: WidgetStateProperty.all(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
// Handle haptic feedback for both iOS and Android
|
||||
if (vibrate == true) {
|
||||
if (Platform.isIOS) {
|
||||
HapticFeedback.selectionClick();
|
||||
} else if (Platform.isAndroid) {
|
||||
await Vibration.vibrate(duration: vibrateDuration);
|
||||
} else {}
|
||||
}
|
||||
|
||||
// Ensure the onPressed callback is called after haptic feedback
|
||||
onPressed();
|
||||
},
|
||||
child: Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppStyle.title.copyWith(color: AppColor.secondaryColor),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
123
lib/views/widgets/error_snakbar.dart
Executable file
123
lib/views/widgets/error_snakbar.dart
Executable file
@@ -0,0 +1,123 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../constant/colors.dart';
|
||||
|
||||
class SnackbarConfig {
|
||||
static const duration = Duration(seconds: 3);
|
||||
static const animationDuration = Duration(milliseconds: 300);
|
||||
static const margin = EdgeInsets.symmetric(horizontal: 16, vertical: 10);
|
||||
static const borderRadius = 12.0;
|
||||
static const elevation = 6.0;
|
||||
|
||||
static final BoxShadow shadow = BoxShadow(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 2),
|
||||
);
|
||||
}
|
||||
|
||||
SnackbarController mySnackeBarError(String message) {
|
||||
// Trigger error haptic feedback
|
||||
HapticFeedback.mediumImpact();
|
||||
|
||||
return Get.snackbar(
|
||||
'Error'.tr,
|
||||
message,
|
||||
backgroundColor: AppColor.redColor.withOpacity(0.95),
|
||||
colorText: AppColor.secondaryColor,
|
||||
icon: const Icon(
|
||||
Icons.error_outline_rounded,
|
||||
color: AppColor.secondaryColor,
|
||||
size: 28,
|
||||
),
|
||||
shouldIconPulse: true,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
margin: SnackbarConfig.margin,
|
||||
borderRadius: SnackbarConfig.borderRadius,
|
||||
duration: SnackbarConfig.duration,
|
||||
animationDuration: SnackbarConfig.animationDuration,
|
||||
forwardAnimationCurve: Curves.easeOutCirc,
|
||||
reverseAnimationCurve: Curves.easeInCirc,
|
||||
boxShadows: [SnackbarConfig.shadow],
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
titleText: Text(
|
||||
'Error'.tr,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
letterSpacing: 0.2,
|
||||
),
|
||||
),
|
||||
messageText: Text(
|
||||
message,
|
||||
style: TextStyle(
|
||||
color: Colors.white.withOpacity(0.95),
|
||||
fontSize: 14,
|
||||
height: 1.3,
|
||||
),
|
||||
),
|
||||
onTap: (_) {
|
||||
HapticFeedback.lightImpact();
|
||||
Get.closeCurrentSnackbar();
|
||||
},
|
||||
isDismissible: true,
|
||||
dismissDirection: DismissDirection.horizontal,
|
||||
overlayBlur: 0.8,
|
||||
overlayColor: Colors.black12,
|
||||
);
|
||||
}
|
||||
|
||||
SnackbarController mySnackbarSuccess(String message) {
|
||||
// Trigger success haptic feedback
|
||||
HapticFeedback.lightImpact();
|
||||
|
||||
return Get.snackbar(
|
||||
'Success'.tr,
|
||||
message,
|
||||
backgroundColor: AppColor.greenColor.withOpacity(0.95),
|
||||
colorText: AppColor.secondaryColor,
|
||||
icon: const Icon(
|
||||
Icons.check_circle_outline_rounded,
|
||||
color: AppColor.secondaryColor,
|
||||
size: 28,
|
||||
),
|
||||
shouldIconPulse: true,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
margin: SnackbarConfig.margin,
|
||||
borderRadius: SnackbarConfig.borderRadius,
|
||||
duration: SnackbarConfig.duration,
|
||||
animationDuration: SnackbarConfig.animationDuration,
|
||||
forwardAnimationCurve: Curves.easeOutCirc,
|
||||
reverseAnimationCurve: Curves.easeInCirc,
|
||||
boxShadows: [SnackbarConfig.shadow],
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
titleText: Text(
|
||||
'Success'.tr,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
letterSpacing: 0.2,
|
||||
),
|
||||
),
|
||||
messageText: Text(
|
||||
message,
|
||||
style: TextStyle(
|
||||
color: Colors.white.withOpacity(0.95),
|
||||
fontSize: 14,
|
||||
height: 1.3,
|
||||
),
|
||||
),
|
||||
onTap: (_) {
|
||||
HapticFeedback.lightImpact();
|
||||
Get.closeCurrentSnackbar();
|
||||
},
|
||||
isDismissible: true,
|
||||
dismissDirection: DismissDirection.horizontal,
|
||||
overlayBlur: 0.8,
|
||||
overlayColor: Colors.black12,
|
||||
);
|
||||
}
|
||||
66
lib/views/widgets/icon_widget_menu.dart
Executable file
66
lib/views/widgets/icon_widget_menu.dart
Executable file
@@ -0,0 +1,66 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
|
||||
import '../../constant/colors.dart';
|
||||
|
||||
class IconWidgetMenu extends StatelessWidget {
|
||||
const IconWidgetMenu({
|
||||
Key? key,
|
||||
required this.onpressed,
|
||||
required this.icon,
|
||||
required this.title,
|
||||
}) : super(key: key);
|
||||
|
||||
final VoidCallback onpressed;
|
||||
final IconData icon;
|
||||
final String title;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: onpressed,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 25),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 50,
|
||||
decoration: const BoxDecoration(
|
||||
color: AppColor.secondaryColor,
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColor.secondaryColor,
|
||||
offset: Offset(-2, -2),
|
||||
blurRadius: 0,
|
||||
spreadRadius: 0,
|
||||
blurStyle: BlurStyle.outer,
|
||||
),
|
||||
BoxShadow(
|
||||
color: AppColor.accentColor,
|
||||
offset: Offset(3, 3),
|
||||
blurRadius: 0,
|
||||
spreadRadius: 0,
|
||||
blurStyle: BlurStyle.outer,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
icon,
|
||||
size: 30,
|
||||
color: AppColor.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
title,
|
||||
style: AppStyle.subtitle,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
73
lib/views/widgets/my_circular_indicator_timer.dart
Executable file
73
lib/views/widgets/my_circular_indicator_timer.dart
Executable file
@@ -0,0 +1,73 @@
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:async';
|
||||
|
||||
class MyCircularProgressIndicatorWithTimer extends StatelessWidget {
|
||||
final Color backgroundColor;
|
||||
final bool isLoading;
|
||||
|
||||
MyCircularProgressIndicatorWithTimer({
|
||||
Key? key,
|
||||
this.backgroundColor = Colors.transparent,
|
||||
required this.isLoading,
|
||||
}) : super(key: key);
|
||||
|
||||
final StreamController<int> _streamController = StreamController<int>();
|
||||
|
||||
void startTimer() {
|
||||
int _timeLeft = 60;
|
||||
Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
if (_timeLeft > 0 && isLoading) {
|
||||
_streamController.add(_timeLeft);
|
||||
_timeLeft--;
|
||||
} else {
|
||||
timer.cancel();
|
||||
_streamController.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (isLoading) {
|
||||
startTimer();
|
||||
}
|
||||
|
||||
return Center(
|
||||
child: Container(
|
||||
width: 200,
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
const Center(child: CircularProgressIndicator()),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Image.asset(
|
||||
'assets/images/logo.gif',
|
||||
width: 140,
|
||||
height: 140,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
StreamBuilder<int>(
|
||||
stream: _streamController.stream,
|
||||
initialData: 60,
|
||||
builder: (context, snapshot) {
|
||||
return Text('${snapshot.data}', style: AppStyle.title);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
50
lib/views/widgets/my_scafold.dart
Executable file
50
lib/views/widgets/my_scafold.dart
Executable file
@@ -0,0 +1,50 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../constant/colors.dart';
|
||||
import '../../constant/style.dart';
|
||||
|
||||
class MyScafolld extends StatelessWidget {
|
||||
const MyScafolld({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.body,
|
||||
this.action = const Icon(
|
||||
Icons.clear,
|
||||
color: AppColor.secondaryColor,
|
||||
),
|
||||
required this.isleading,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final List<Widget> body;
|
||||
final Widget action;
|
||||
final bool isleading;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColor.secondaryColor,
|
||||
appBar: AppBar(
|
||||
backgroundColor: AppColor.secondaryColor,
|
||||
elevation: 0,
|
||||
leading: isleading
|
||||
? IconButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.arrow_back_ios_new,
|
||||
color: AppColor.primaryColor,
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
actions: [action],
|
||||
title: Text(
|
||||
title,
|
||||
style: AppStyle.title.copyWith(fontSize: 30),
|
||||
),
|
||||
),
|
||||
body: SafeArea(child: Stack(children: body)));
|
||||
}
|
||||
}
|
||||
94
lib/views/widgets/my_textField.dart
Executable file
94
lib/views/widgets/my_textField.dart
Executable file
@@ -0,0 +1,94 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:sefer_driver/constant/box_name.dart';
|
||||
|
||||
class MyTextForm extends StatelessWidget {
|
||||
const MyTextForm({
|
||||
Key? key,
|
||||
required this.controller,
|
||||
required this.label,
|
||||
required this.hint,
|
||||
required this.type,
|
||||
}) : super(key: key);
|
||||
|
||||
final TextEditingController controller;
|
||||
final String label, hint;
|
||||
final TextInputType type;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: SizedBox(
|
||||
width: Get.width * .8,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
label.tr,
|
||||
style: TextStyle(
|
||||
color: CupertinoColors.label,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
CupertinoTextField(
|
||||
controller: controller,
|
||||
keyboardType: type,
|
||||
placeholder: hint.tr,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: CupertinoColors.systemBackground,
|
||||
border: Border.all(color: CupertinoColors.systemGrey4),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
style: const TextStyle(color: CupertinoColors.label),
|
||||
placeholderStyle:
|
||||
const TextStyle(color: CupertinoColors.placeholderText),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
ValueListenableBuilder<TextEditingValue>(
|
||||
valueListenable: controller,
|
||||
builder: (context, value, child) {
|
||||
String? errorText = _getErrorText(value.text);
|
||||
return errorText != null
|
||||
? Text(
|
||||
errorText,
|
||||
style: const TextStyle(
|
||||
color: CupertinoColors.destructiveRed,
|
||||
fontSize: 12),
|
||||
)
|
||||
: const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String? _getErrorText(String value) {
|
||||
if (value.isEmpty) {
|
||||
return '${'Please enter'.tr} $label'.tr;
|
||||
}
|
||||
|
||||
if (type == TextInputType.emailAddress) {
|
||||
if (!value.contains('@')) {
|
||||
return 'Please enter a valid email.'.tr;
|
||||
}
|
||||
} else if (type == TextInputType.phone) {
|
||||
final box = GetStorage();
|
||||
if (box.read(BoxName.countryCode) == 'Egypt') {
|
||||
if (value.length != 11) {
|
||||
return 'Please enter a valid phone number.'.tr;
|
||||
}
|
||||
} else if (value.length != 10) {
|
||||
return 'Please enter a valid phone number.'.tr;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
132
lib/views/widgets/mycircular.dart
Executable file
132
lib/views/widgets/mycircular.dart
Executable file
@@ -0,0 +1,132 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MyCircularProgressIndicator extends StatefulWidget {
|
||||
final Color backgroundColor;
|
||||
final double size;
|
||||
final Color progressColor;
|
||||
final double strokeWidth;
|
||||
|
||||
const MyCircularProgressIndicator({
|
||||
super.key,
|
||||
this.backgroundColor = Colors.transparent,
|
||||
this.size = 110,
|
||||
this.progressColor = Colors.blue,
|
||||
this.strokeWidth = 3.0,
|
||||
});
|
||||
|
||||
@override
|
||||
State<MyCircularProgressIndicator> createState() =>
|
||||
_MyCircularProgressIndicatorState();
|
||||
}
|
||||
|
||||
class _MyCircularProgressIndicatorState
|
||||
extends State<MyCircularProgressIndicator>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _controller;
|
||||
late Animation<double> _scaleAnimation;
|
||||
late Animation<double> _rotationAnimation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = AnimationController(
|
||||
duration: const Duration(seconds: 2),
|
||||
vsync: this,
|
||||
)..repeat(reverse: true);
|
||||
|
||||
_scaleAnimation = Tween<double>(
|
||||
begin: 0.95,
|
||||
end: 1.05,
|
||||
).animate(CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Curves.easeInOut,
|
||||
));
|
||||
|
||||
_rotationAnimation = Tween<double>(
|
||||
begin: 0,
|
||||
end: 2,
|
||||
).animate(CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Curves.linear,
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: AnimatedBuilder(
|
||||
animation: _controller,
|
||||
builder: (context, child) {
|
||||
return Transform.scale(
|
||||
scale: _scaleAnimation.value,
|
||||
child: Container(
|
||||
width: widget.size,
|
||||
height: widget.size,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.backgroundColor,
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: widget.progressColor.withAlpha(30),
|
||||
blurRadius: 12,
|
||||
spreadRadius: 2,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
// Outer rotating progress indicator
|
||||
Transform.rotate(
|
||||
angle: _rotationAnimation.value * 3.14,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: widget.strokeWidth,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
widget.progressColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
// Inner static progress indicator
|
||||
CircularProgressIndicator(
|
||||
strokeWidth: widget.strokeWidth * 0.7,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
widget.progressColor.withAlpha(150),
|
||||
),
|
||||
),
|
||||
// Logo container with scale animation
|
||||
ScaleTransition(
|
||||
scale: Tween<double>(
|
||||
begin: 0.9,
|
||||
end: 1.0,
|
||||
).animate(CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Curves.easeInOut,
|
||||
)),
|
||||
child: Container(
|
||||
width: widget.size * 0.7,
|
||||
height: widget.size * 0.7,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: widget.backgroundColor,
|
||||
),
|
||||
child: Image.asset(
|
||||
'assets/images/logo.gif',
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
245
lib/views/widgets/mydialoug.dart
Executable file
245
lib/views/widgets/mydialoug.dart
Executable file
@@ -0,0 +1,245 @@
|
||||
import 'dart:ui';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.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/functions/tts.dart';
|
||||
|
||||
class DialogConfig {
|
||||
static const Duration animationDuration = Duration(milliseconds: 200);
|
||||
static const double blurStrength = 8.0;
|
||||
static const double cornerRadius = 14.0;
|
||||
static final BoxDecoration decoration = BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(cornerRadius),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withAlpha(38), // 0.15 opacity
|
||||
blurRadius: 16,
|
||||
offset: const Offset(0, 8),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
class MyDialog extends GetxController {
|
||||
void getDialog(String title, String? midTitle, VoidCallback onPressed) {
|
||||
final textToSpeechController = Get.put(TextToSpeechController());
|
||||
|
||||
HapticFeedback.mediumImpact();
|
||||
|
||||
Get.dialog(
|
||||
TweenAnimationBuilder<double>(
|
||||
duration: DialogConfig.animationDuration,
|
||||
tween: Tween(begin: 0.0, end: 1.0),
|
||||
builder: (context, value, child) {
|
||||
return Transform.scale(
|
||||
scale: 0.95 + (0.05 * value),
|
||||
child: Opacity(opacity: value, child: child),
|
||||
);
|
||||
},
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(
|
||||
sigmaX: DialogConfig.blurStrength,
|
||||
sigmaY: DialogConfig.blurStrength,
|
||||
),
|
||||
child: Theme(
|
||||
data: ThemeData.light().copyWith(
|
||||
dialogBackgroundColor: CupertinoColors.systemBackground,
|
||||
),
|
||||
child: CupertinoAlertDialog(
|
||||
title: Column(
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: AppStyle.title.copyWith(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: -0.5,
|
||||
color: AppColor.primaryColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
content: Column(
|
||||
children: [
|
||||
CupertinoButton(
|
||||
padding: const EdgeInsets.all(8),
|
||||
onPressed: () async {
|
||||
HapticFeedback.selectionClick();
|
||||
await textToSpeechController.speakText(title);
|
||||
await textToSpeechController.speakText(midTitle!);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
AppColor.primaryColor.withAlpha(26), // 0.1 opacity
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(
|
||||
CupertinoIcons.speaker_2_fill,
|
||||
color: AppColor.primaryColor,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
midTitle!,
|
||||
style: AppStyle.title.copyWith(
|
||||
fontSize: 16,
|
||||
height: 1.3,
|
||||
color: Colors.black87,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
CupertinoDialogAction(
|
||||
onPressed: () {
|
||||
HapticFeedback.lightImpact();
|
||||
Get.back();
|
||||
},
|
||||
child: Text(
|
||||
'Cancel',
|
||||
style: TextStyle(
|
||||
color: AppColor.redColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 17,
|
||||
),
|
||||
),
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
onPressed: () {
|
||||
HapticFeedback.mediumImpact();
|
||||
onPressed();
|
||||
},
|
||||
child: Text(
|
||||
'OK'.tr,
|
||||
style: TextStyle(
|
||||
color: AppColor.greenColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 17,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
barrierDismissible: true,
|
||||
barrierColor: Colors.black.withAlpha(102), // 0.4 opacity
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyDialogContent extends GetxController {
|
||||
void getDialog(String title, Widget? content, VoidCallback onPressed) {
|
||||
final textToSpeechController = Get.put(TextToSpeechController());
|
||||
|
||||
HapticFeedback.mediumImpact();
|
||||
|
||||
Get.dialog(
|
||||
TweenAnimationBuilder<double>(
|
||||
duration: DialogConfig.animationDuration,
|
||||
tween: Tween(begin: 0.0, end: 1.0),
|
||||
builder: (context, value, child) {
|
||||
return Transform.scale(
|
||||
scale: 0.95 + (0.05 * value),
|
||||
child: Opacity(opacity: value, child: child),
|
||||
);
|
||||
},
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(
|
||||
sigmaX: DialogConfig.blurStrength,
|
||||
sigmaY: DialogConfig.blurStrength,
|
||||
),
|
||||
child: Theme(
|
||||
data: ThemeData.light().copyWith(
|
||||
dialogBackgroundColor: CupertinoColors.systemBackground,
|
||||
),
|
||||
child: CupertinoAlertDialog(
|
||||
title: Column(
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: AppStyle.title.copyWith(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: -0.5,
|
||||
color: AppColor.primaryColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
content: Column(
|
||||
children: [
|
||||
CupertinoButton(
|
||||
padding: const EdgeInsets.all(8),
|
||||
onPressed: () async {
|
||||
HapticFeedback.selectionClick();
|
||||
await textToSpeechController.speakText(title);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
AppColor.primaryColor.withAlpha(26), // 0.1 opacity
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(
|
||||
CupertinoIcons.headphones,
|
||||
color: AppColor.primaryColor,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
content!,
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
CupertinoDialogAction(
|
||||
onPressed: () {
|
||||
HapticFeedback.lightImpact();
|
||||
Get.back();
|
||||
},
|
||||
child: Text(
|
||||
'Cancel',
|
||||
style: TextStyle(
|
||||
color: AppColor.redColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 17,
|
||||
),
|
||||
),
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
onPressed: () {
|
||||
HapticFeedback.mediumImpact();
|
||||
onPressed();
|
||||
},
|
||||
child: Text(
|
||||
'OK'.tr,
|
||||
style: TextStyle(
|
||||
color: AppColor.greenColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 17,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
barrierDismissible: true,
|
||||
barrierColor: Colors.black.withAlpha(102), // 0.4 opacity
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user