flutter(rider): rebuild lib on Cubit/Bloc + Tripz backend (core + auth slice)
- keep store identity (com.mobileapp.store.ride, shorebird, firebase, fonts); old lib -> lib_old_getx - core: config/di/router(go_router)/theme/api_client(dio+tenant+bearer)/token_store(secure) - auth: repo + AuthCubit + OTP phone/otp screens; home: flutter_map (OSM placeholder, انطلق TODO) - android usesCleartextTraffic=true for http API; maps=flutter_map (no Google), state=Cubit Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,233 +0,0 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class AboutPage extends StatelessWidget {
|
||||
const AboutPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text('About Us'.tr),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// Company Logo
|
||||
Center(
|
||||
child: Image.asset(
|
||||
'assets/images/logo.gif', // Replace with your logo image asset path
|
||||
height: 80.0,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Company Name and Introduction
|
||||
Text(
|
||||
'Intaleq LLC',
|
||||
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
"Syria's pioneering ride-sharing service, proudly developed by Arabian and local owners. We prioritize being near you – both our valued passengers and our dedicated captains."
|
||||
.tr,
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
|
||||
// Key Features Section
|
||||
Text(
|
||||
'Why Choose Intaleq?'.tr,
|
||||
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
|
||||
// Nearest Availability
|
||||
Row(
|
||||
children: [
|
||||
const Icon(CupertinoIcons.location_solid,
|
||||
color: CupertinoColors.activeBlue),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Closest to You'.tr,
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'We connect you with the nearest drivers for faster pickups and quicker journeys.'
|
||||
.tr,
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.textStyle
|
||||
.copyWith(color: CupertinoColors.secondaryLabel),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// High-Level Security
|
||||
Row(
|
||||
children: [
|
||||
const Icon(CupertinoIcons.shield_fill,
|
||||
color: CupertinoColors.activeGreen),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Uncompromising Security'.tr,
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Icon(CupertinoIcons.person_2_fill,
|
||||
size: 18, color: CupertinoColors.activeGreen),
|
||||
const SizedBox(width: 5),
|
||||
Text(
|
||||
'Lady Captains Available'.tr,
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.textStyle
|
||||
.copyWith(fontSize: 15),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(CupertinoIcons.recordingtape,
|
||||
size: 18, color: CupertinoColors.activeGreen),
|
||||
const SizedBox(width: 5),
|
||||
Text(
|
||||
'Recorded Trips (Voice & AI Analysis)'.tr,
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.textStyle
|
||||
.copyWith(fontSize: 15),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Fast Support
|
||||
Row(
|
||||
children: [
|
||||
const Icon(CupertinoIcons.bolt_horizontal_fill,
|
||||
color: CupertinoColors.systemOrange),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Fastest Complaint Response'.tr,
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'Our dedicated customer service team ensures swift resolution of any issues.'
|
||||
.tr,
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.textStyle
|
||||
.copyWith(color: CupertinoColors.secondaryLabel),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Affordable Pricing
|
||||
Row(
|
||||
children: [
|
||||
const Icon(CupertinoIcons.money_dollar_circle_fill,
|
||||
color: CupertinoColors.activeBlue),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Affordable for Everyone'.tr,
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'Enjoy competitive prices across all trip options, making travel accessible.'
|
||||
.tr,
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.textStyle
|
||||
.copyWith(color: CupertinoColors.secondaryLabel),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Trip Options
|
||||
Row(
|
||||
children: [
|
||||
const Icon(CupertinoIcons.car_detailed,
|
||||
color: CupertinoColors.systemPurple),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Variety of Trip Choices'.tr,
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'Choose the trip option that perfectly suits your needs and preferences.'
|
||||
.tr,
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.textStyle
|
||||
.copyWith(color: CupertinoColors.secondaryLabel),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Passenger Choice
|
||||
Row(
|
||||
children: [
|
||||
Icon(CupertinoIcons.hand_draw_fill,
|
||||
color: CupertinoColors.systemGreen),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Your Choice, Our Priority'.tr,
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'Because we are near, you have the flexibility to choose the ride that works best for you.'
|
||||
.tr,
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.textStyle
|
||||
.copyWith(color: CupertinoColors.secondaryLabel),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
import 'package:Tripz/constant/colors.dart';
|
||||
import 'package:Tripz/constant/style.dart';
|
||||
// ignore: unused_import
|
||||
import 'package:Tripz/controller/functions/launch.dart';
|
||||
import 'package:Tripz/views/widgets/my_scafold.dart';
|
||||
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 '../../../controller/functions/tts.dart';
|
||||
import '../../../controller/home/contact_us_controller.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 and most reliable ride-sharing app designed especially for passengers in Syria. We provide a comfortable, respectful, and affordable riding experience with features that prioritize your safety and convenience. Our trusted captains are verified, insured, and supported by regular car maintenance carried out by top engineers. We also offer on-road support services to make sure every trip is smooth and worry-free. With Tripz, you enjoy quality, safety, and peace of mind—every time you ride.'
|
||||
.tr);
|
||||
},
|
||||
icon: const Icon(Icons.headphones),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
'Tripz is the safest and most reliable ride-sharing app designed especially for passengers in Syria. We provide a comfortable, respectful, and affordable riding experience with features that prioritize your safety and convenience. Our trusted captains are verified, insured, and supported by regular car maintenance carried out by top engineers. We also offer on-road support services to make sure every trip is smooth and worry-free. With Tripz, you enjoy quality, safety, and peace of mind—every time you ride.'
|
||||
.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 10:00 - 16: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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,228 +0,0 @@
|
||||
import 'package:Tripz/views/home/HomePage/contact_us.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class FrequentlyQuestionsPage extends StatelessWidget {
|
||||
const FrequentlyQuestionsPage({super.key});
|
||||
|
||||
void _showAnswerDialog(BuildContext context, String question, String answer) {
|
||||
showCupertinoDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) => CupertinoAlertDialog(
|
||||
title: Text(question,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold, color: Colors.indigo)),
|
||||
content: Text(answer),
|
||||
actions: <CupertinoDialogAction>[
|
||||
CupertinoDialogAction(
|
||||
isDefaultAction: true,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text('Close'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
backgroundColor: Colors.indigo,
|
||||
middle: Text(
|
||||
'Frequently Asked Questions'.tr,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: ListView(
|
||||
children: <Widget>[
|
||||
CupertinoListSection.insetGrouped(
|
||||
header: Text(
|
||||
'Getting Started'.tr,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
children: <CupertinoListTile>[
|
||||
CupertinoListTile(
|
||||
leading: const Icon(
|
||||
CupertinoIcons.car_detailed,
|
||||
color: Colors.indigo,
|
||||
),
|
||||
title: Text('How do I request a ride?'.tr),
|
||||
trailing: const CupertinoListTileChevron(),
|
||||
onTap: () => _showAnswerDialog(
|
||||
context,
|
||||
'How do I request a ride?'.tr,
|
||||
'Simply open the Tripz app, enter your destination, and tap "Request Ride". The app will connect you with a nearby driver.'
|
||||
.tr,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
CupertinoListSection.insetGrouped(
|
||||
header: Text(
|
||||
'Vehicle Options'.tr,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
children: <CupertinoListTile>[
|
||||
CupertinoListTile(
|
||||
leading: const Icon(
|
||||
CupertinoIcons.car_fill,
|
||||
color: Colors.blue,
|
||||
),
|
||||
title: Text('What types of vehicles are available?'.tr),
|
||||
trailing: const CupertinoListTileChevron(),
|
||||
onTap: () => _showAnswerDialog(
|
||||
context,
|
||||
'What types of vehicles are available?'.tr,
|
||||
'Tripz offers a variety of options including Economy, Comfort, and Luxury to suit your needs and budget.'
|
||||
.tr,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
CupertinoListSection.insetGrouped(
|
||||
header: Text(
|
||||
'Payments'.tr,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
children: <CupertinoListTile>[
|
||||
CupertinoListTile(
|
||||
leading: const Icon(
|
||||
CupertinoIcons.creditcard,
|
||||
color: Colors.green,
|
||||
),
|
||||
title: Text('How can I pay for my ride?'.tr),
|
||||
trailing: const CupertinoListTileChevron(),
|
||||
onTap: () => _showAnswerDialog(
|
||||
context,
|
||||
'How can I pay for my ride?'.tr,
|
||||
'You can pay for your ride using cash or credit/debit card. You can select your preferred payment method before confirming your ride.'
|
||||
.tr,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
CupertinoListSection.insetGrouped(
|
||||
header: Text(
|
||||
'Ride Management'.tr,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
children: <CupertinoListTile>[
|
||||
CupertinoListTile(
|
||||
leading: const Icon(
|
||||
CupertinoIcons.xmark_circle_fill,
|
||||
color: Colors.red,
|
||||
),
|
||||
title: Text('Can I cancel my ride?'.tr),
|
||||
trailing: const CupertinoListTileChevron(),
|
||||
onTap: () => _showAnswerDialog(
|
||||
context,
|
||||
'Can I cancel my ride?'.tr,
|
||||
'Yes, you can cancel your ride, but please note that cancellation fees may apply depending on how far in advance you cancel.'
|
||||
.tr,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
CupertinoListSection.insetGrouped(
|
||||
header: Text(
|
||||
'For Drivers'.tr,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
children: <CupertinoListTile>[
|
||||
CupertinoListTile(
|
||||
leading: const Icon(
|
||||
CupertinoIcons.person_crop_circle_fill,
|
||||
color: Colors.orange,
|
||||
),
|
||||
title: Text('Driver Registration & Requirements'.tr),
|
||||
trailing: const CupertinoListTileChevron(),
|
||||
onTap: () => showCupertinoDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) => CupertinoAlertDialog(
|
||||
title: Text('Driver Registration'.tr,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.indigo)),
|
||||
content: Text(
|
||||
'To register as a driver or learn about the requirements, please visit our website or contact Tripz support directly.'
|
||||
.tr),
|
||||
actions: <CupertinoDialogAction>[
|
||||
CupertinoDialogAction(
|
||||
isDefaultAction: true,
|
||||
onPressed: () {
|
||||
Get.to(() => ContactUsPage());
|
||||
// Optionally, you can open a URL here
|
||||
},
|
||||
child: Text('Visit Website/Contact Support'.tr),
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text('Close'.tr),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
CupertinoListSection.insetGrouped(
|
||||
header: Text(
|
||||
'Communication'.tr,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
children: <CupertinoListTile>[
|
||||
CupertinoListTile(
|
||||
leading: const Icon(
|
||||
CupertinoIcons.chat_bubble_2_fill,
|
||||
color: Colors.purple,
|
||||
),
|
||||
title: Text(
|
||||
'How do I communicate with the other party (passenger/driver)?'
|
||||
.tr),
|
||||
trailing: const CupertinoListTileChevron(),
|
||||
onTap: () => _showAnswerDialog(
|
||||
context,
|
||||
'How do I communicate with the other party (passenger/driver)?'
|
||||
.tr,
|
||||
'You can communicate with your driver or passenger through the in-app chat feature once a ride is confirmed.'
|
||||
.tr,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
CupertinoListSection.insetGrouped(
|
||||
header: Text(
|
||||
'Safety & Security'.tr,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
children: <CupertinoListTile>[
|
||||
CupertinoListTile(
|
||||
leading: const Icon(
|
||||
CupertinoIcons.shield_fill,
|
||||
color: Colors.teal,
|
||||
),
|
||||
title: Text('What safety measures does Tripz offer?'.tr),
|
||||
trailing: const CupertinoListTileChevron(),
|
||||
onTap: () => _showAnswerDialog(
|
||||
context,
|
||||
'What safety measures does Tripz offer?'.tr,
|
||||
'Tripz offers various safety features including driver verification, in-app trip tracking, emergency contact options, and the ability to share your trip status with trusted contacts.'
|
||||
.tr,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20), // Add some bottom padding
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,568 +0,0 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../controller/home/profile/invit_controller.dart';
|
||||
import '../../../print.dart';
|
||||
|
||||
class ShareAppPage 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: [
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: _buildPassengerTab(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 your friends 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 _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();
|
||||
Log.print('contacts: ${controller.contacts}');
|
||||
if (controller.contacts.isNotEmpty) {
|
||||
_showContactsDialog(Get
|
||||
.context!); // Show contacts dialog after loading contacts
|
||||
} else {
|
||||
Get.snackbar(
|
||||
'No contacts available'.tr,
|
||||
'Please add contacts to your phone.'.tr,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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 _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 _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 / 2 ${'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) {
|
||||
Get.defaultDialog(
|
||||
title: 'Choose from contact'.tr,
|
||||
content: SizedBox(
|
||||
height: 400,
|
||||
width: 400,
|
||||
child: Column(
|
||||
children: [
|
||||
// Header with cancel and title
|
||||
// Container(
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
// decoration: const BoxDecoration(
|
||||
// borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
// color: CupertinoColors.systemGrey6,
|
||||
// ),
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// children: [
|
||||
// CupertinoButton(
|
||||
// padding: EdgeInsets.zero,
|
||||
// child: Text(
|
||||
// 'Cancel'.tr,
|
||||
// style: const TextStyle(color: CupertinoColors.systemBlue),
|
||||
// ),
|
||||
// onPressed: () => Navigator.pop(context),
|
||||
// ),
|
||||
// Container(
|
||||
// child: Text('Choose from contact'.tr,
|
||||
// style: AppStyle.title)),
|
||||
// const SizedBox(width: 60), // Balance for Cancel button
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
|
||||
// Contact list
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: controller.contactMaps.length,
|
||||
itemBuilder: (context, index) {
|
||||
final contact = controller.contactMaps[index];
|
||||
return CupertinoButton(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: () {
|
||||
controller.selectPhone(contact['phones'].toString());
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16, vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: CupertinoColors.systemBackground,
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: CupertinoColors.separator.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
// Display contact name and phone number
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
contact['name'],
|
||||
style: const TextStyle(
|
||||
color: CupertinoColors.label,
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
(contact['phones'][0].toString()),
|
||||
style: const TextStyle(
|
||||
color: CupertinoColors.secondaryLabel,
|
||||
fontSize: 15,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Chevron icon for selection
|
||||
const Icon(
|
||||
CupertinoIcons.chevron_forward,
|
||||
color: CupertinoColors.systemGrey2,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
// showCupertinoModalPopup(
|
||||
// context: context,
|
||||
// builder: (BuildContext context) => Container(
|
||||
// height: 400,
|
||||
// decoration: BoxDecoration(
|
||||
// color: CupertinoColors.systemBackground,
|
||||
// borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
|
||||
// boxShadow: [
|
||||
// BoxShadow(
|
||||
// color: CupertinoColors.black.withOpacity(0.2),
|
||||
// offset: const Offset(0, -4),
|
||||
// blurRadius: 10,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// child: Column(
|
||||
// children: [
|
||||
// // Header with cancel and title
|
||||
// Container(
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
// decoration: const BoxDecoration(
|
||||
// borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
// color: CupertinoColors.systemGrey6,
|
||||
// ),
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// children: [
|
||||
// CupertinoButton(
|
||||
// padding: EdgeInsets.zero,
|
||||
// child: Text(
|
||||
// 'Cancel'.tr,
|
||||
// style: const TextStyle(color: CupertinoColors.systemBlue),
|
||||
// ),
|
||||
// onPressed: () => Navigator.pop(context),
|
||||
// ),
|
||||
// Container(
|
||||
// child: Text('Choose from contact'.tr,
|
||||
// style: AppStyle.title)),
|
||||
// const SizedBox(width: 60), // Balance for Cancel button
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
|
||||
// // Contact list
|
||||
// Expanded(
|
||||
// child: ListView.builder(
|
||||
// itemCount: controller.contactMaps.length,
|
||||
// itemBuilder: (context, index) {
|
||||
// final contact = controller.contactMaps[index];
|
||||
// return CupertinoButton(
|
||||
// padding: EdgeInsets.zero,
|
||||
// onPressed: () {
|
||||
// controller.selectPhone(contact['phones'].toString());
|
||||
// },
|
||||
// child: Container(
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// horizontal: 16, vertical: 12),
|
||||
// decoration: BoxDecoration(
|
||||
// color: CupertinoColors.systemBackground,
|
||||
// border: Border(
|
||||
// bottom: BorderSide(
|
||||
// color: CupertinoColors.separator.withOpacity(0.5),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// child: Row(
|
||||
// children: [
|
||||
// // Display contact name and phone number
|
||||
// Expanded(
|
||||
// child: Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// Text(
|
||||
// contact['name'],
|
||||
// style: const TextStyle(
|
||||
// color: CupertinoColors.label,
|
||||
// fontSize: 17,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// ),
|
||||
// ),
|
||||
// Text(
|
||||
// controller.formatPhoneNumber(
|
||||
// contact['phones'][0].toString()),
|
||||
// style: const TextStyle(
|
||||
// color: CupertinoColors.secondaryLabel,
|
||||
// fontSize: 15,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// // Chevron icon for selection
|
||||
// const Icon(
|
||||
// CupertinoIcons.chevron_forward,
|
||||
// color: CupertinoColors.systemGrey2,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:Tripz/controller/home/trip_monitor_controller.dart';
|
||||
import 'package:Tripz/views/widgets/my_scafold.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:vibration/vibration.dart';
|
||||
|
||||
import '../../../../constant/colors.dart';
|
||||
import '../../../../constant/style.dart';
|
||||
import '../../../widgets/elevated_btn.dart';
|
||||
|
||||
class TripMonitor extends StatelessWidget {
|
||||
const TripMonitor({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final params = Get.parameters;
|
||||
// Use params to initialize your controller or pass data
|
||||
Get.put(TripMonitorController()).init();
|
||||
return GetBuilder<TripMonitorController>(builder: (tripMonitorController) {
|
||||
return MyScafolld(
|
||||
title: 'Trip Monitor'.tr,
|
||||
body: [
|
||||
GoogleMap(
|
||||
onMapCreated: tripMonitorController.onMapCreated,
|
||||
initialCameraPosition: CameraPosition(
|
||||
// bearing: 45,
|
||||
target: tripMonitorController.parentLocation,
|
||||
zoom: 16,
|
||||
tilt: 40,
|
||||
),
|
||||
// onCameraMove: (position) {},
|
||||
markers: {
|
||||
Marker(
|
||||
markerId: MarkerId('start'.tr),
|
||||
position: tripMonitorController.parentLocation,
|
||||
draggable: true,
|
||||
icon: tripMonitorController.tripData['message'][0]['model']
|
||||
.contains('دراجة')
|
||||
? tripMonitorController.motoIcon
|
||||
: tripMonitorController.tripData['message'][0]['model']
|
||||
['gender'] ==
|
||||
'Male'
|
||||
? tripMonitorController.carIcon
|
||||
: tripMonitorController.ladyIcon,
|
||||
rotation: tripMonitorController.rotation,
|
||||
),
|
||||
},
|
||||
),
|
||||
speedCircle()
|
||||
],
|
||||
isleading: true,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
GetBuilder<TripMonitorController> speedCircle() {
|
||||
if (Get.find<TripMonitorController>().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<TripMonitorController>(
|
||||
builder: (tripMonitorController) {
|
||||
return Positioned(
|
||||
bottom: 25,
|
||||
right: 100,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: tripMonitorController.speed > 100
|
||||
? Colors.red
|
||||
: AppColor.secondaryColor,
|
||||
border: Border.all(width: 3, color: AppColor.redColor),
|
||||
),
|
||||
height: 60,
|
||||
width: 60,
|
||||
child: Center(
|
||||
child: Text(
|
||||
tripMonitorController.speed.toStringAsFixed(0),
|
||||
style: AppStyle.number,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:Tripz/controller/home/trip_monitor_controller.dart';
|
||||
import 'package:Tripz/views/widgets/my_scafold.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:vibration/vibration.dart';
|
||||
|
||||
import '../../../../constant/colors.dart';
|
||||
import '../../../../constant/style.dart';
|
||||
import '../../../widgets/elevated_btn.dart';
|
||||
|
||||
class TripMonitor extends StatelessWidget {
|
||||
const TripMonitor({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final params = Get.parameters;
|
||||
final arguments = Get.arguments as Map<String, dynamic>?;
|
||||
|
||||
// Use params or arguments to initialize your controller
|
||||
final controller = Get.put(TripMonitorController());
|
||||
controller.init(
|
||||
rideId: params['rideId'] ?? arguments?['rideId'],
|
||||
driverId: params['driverId'] ?? arguments?['driverId'],
|
||||
);
|
||||
return GetBuilder<TripMonitorController>(builder: (tripMonitorController) {
|
||||
return MyScafolld(
|
||||
title: 'Trip Monitor'.tr,
|
||||
body: [
|
||||
GoogleMap(
|
||||
onMapCreated: tripMonitorController.onMapCreated,
|
||||
initialCameraPosition: CameraPosition(
|
||||
// bearing: 45,
|
||||
target: tripMonitorController.parentLocation,
|
||||
zoom: 16,
|
||||
tilt: 40,
|
||||
),
|
||||
// onCameraMove: (position) {},
|
||||
markers: {
|
||||
Marker(
|
||||
markerId: MarkerId('start'.tr),
|
||||
position: tripMonitorController.parentLocation,
|
||||
draggable: true,
|
||||
icon: tripMonitorController.tripData['message'][0]['model']
|
||||
.contains('دراجة')
|
||||
? tripMonitorController.motoIcon
|
||||
: tripMonitorController.tripData['message'][0]['model']
|
||||
['gender'] ==
|
||||
'Male'
|
||||
? tripMonitorController.carIcon
|
||||
: tripMonitorController.ladyIcon,
|
||||
rotation: tripMonitorController.rotation,
|
||||
),
|
||||
},
|
||||
),
|
||||
speedCircle()
|
||||
],
|
||||
isleading: true,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
GetBuilder<TripMonitorController> speedCircle() {
|
||||
if (Get.find<TripMonitorController>().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<TripMonitorController>(
|
||||
builder: (tripMonitorController) {
|
||||
return Positioned(
|
||||
bottom: 25,
|
||||
right: 100,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: tripMonitorController.speed > 100
|
||||
? Colors.red
|
||||
: AppColor.secondaryColor,
|
||||
border: Border.all(width: 3, color: AppColor.redColor),
|
||||
),
|
||||
height: 60,
|
||||
width: 60,
|
||||
child: Center(
|
||||
child: Text(
|
||||
tripMonitorController.speed.toStringAsFixed(0),
|
||||
style: AppStyle.number,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1,263 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
import '../../../controller/functions/audio_record1.dart';
|
||||
|
||||
class TripsRecordedPage extends StatelessWidget {
|
||||
const TripsRecordedPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Ensure the controller is available.
|
||||
// If it's not initialized elsewhere, you can use Get.put() or Get.lazyPut() here.
|
||||
// Get.lazyPut(() => AudioRecorderController());
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Trips recorded'.tr),
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 1,
|
||||
actions: [
|
||||
GetBuilder<AudioRecorderController>(
|
||||
builder: (controller) => IconButton(
|
||||
tooltip: 'Delete All'.tr,
|
||||
icon: const Icon(Icons.delete_sweep_outlined),
|
||||
onPressed: () {
|
||||
_showDeleteConfirmation(context, controller, isDeleteAll: true);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
body: GetBuilder<AudioRecorderController>(
|
||||
builder: (controller) {
|
||||
return Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildRecordingsList(controller),
|
||||
),
|
||||
// Show player controls only when a file is selected
|
||||
if (controller.selectedFilePath != null)
|
||||
_buildPlayerControls(context, controller),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Builds the list of recorded audio files.
|
||||
Widget _buildRecordingsList(AudioRecorderController controller) {
|
||||
return FutureBuilder<List<String>>(
|
||||
future: controller.getRecordedFiles(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
if (snapshot.hasError) {
|
||||
return Center(child: Text('Error: ${snapshot.error}'.tr));
|
||||
}
|
||||
if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.mic_off_outlined, size: 80, color: Colors.grey[400]),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'No Recordings Found'.tr,
|
||||
style: TextStyle(fontSize: 18, color: Colors.grey[600]),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 40.0),
|
||||
child: Text(
|
||||
'Record your trips to see them here.'.tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.grey[500]),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final recordedFiles = snapshot.data!;
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.only(top: 8, bottom: 8),
|
||||
itemCount: recordedFiles.length,
|
||||
itemBuilder: (context, index) {
|
||||
final file = recordedFiles[index];
|
||||
final fileName = path.basename(file);
|
||||
final isSelected = controller.selectedFilePath == file;
|
||||
|
||||
return Card(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
|
||||
elevation: isSelected ? 4 : 1,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12)),
|
||||
child: ListTile(
|
||||
leading: Icon(
|
||||
isSelected && controller.isPlaying
|
||||
? Icons.pause_circle_filled
|
||||
: Icons.play_circle_fill,
|
||||
color:
|
||||
isSelected ? Theme.of(context).primaryColor : Colors.grey,
|
||||
size: 40,
|
||||
),
|
||||
title: Text(fileName,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500)),
|
||||
subtitle: Text("Audio Recording".tr),
|
||||
onTap: () {
|
||||
if (isSelected) {
|
||||
controller.isPlaying
|
||||
? controller.pausePlayback()
|
||||
: controller.resumePlayback();
|
||||
} else {
|
||||
controller.playRecordedFile(file);
|
||||
}
|
||||
},
|
||||
selected: isSelected,
|
||||
selectedTileColor:
|
||||
Theme.of(context).primaryColor.withOpacity(0.1),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Builds the player UI at the bottom of the screen.
|
||||
Widget _buildPlayerControls(
|
||||
BuildContext context, AudioRecorderController controller) {
|
||||
final fileName = path.basename(controller.selectedFilePath!);
|
||||
final positionText = Duration(seconds: controller.currentPosition.toInt())
|
||||
.toString()
|
||||
.split('.')
|
||||
.first
|
||||
.padLeft(8, '0')
|
||||
.substring(3);
|
||||
final durationText = Duration(seconds: controller.totalDuration.toInt())
|
||||
.toString()
|
||||
.split('.')
|
||||
.first
|
||||
.padLeft(8, '0')
|
||||
.substring(3);
|
||||
|
||||
return Material(
|
||||
elevation: 10,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(fileName,
|
||||
style:
|
||||
const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
|
||||
textAlign: TextAlign.center),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Text(positionText),
|
||||
Expanded(
|
||||
child: Slider(
|
||||
value: (controller.totalDuration > 0)
|
||||
? controller.currentPosition / controller.totalDuration
|
||||
: 0.0,
|
||||
onChanged: (value) {
|
||||
final newPosition = value * controller.totalDuration;
|
||||
controller.audioPlayer
|
||||
.seek(Duration(seconds: newPosition.toInt()));
|
||||
},
|
||||
),
|
||||
),
|
||||
Text(durationText),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.share_outlined),
|
||||
tooltip: 'Share'.tr,
|
||||
onPressed: () {
|
||||
Share.shareXFiles([XFile(controller.selectedFilePath!)]);
|
||||
},
|
||||
iconSize: 28,
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(controller.isPlaying
|
||||
? Icons.pause_circle_filled
|
||||
: Icons.play_circle_filled),
|
||||
onPressed: () {
|
||||
controller.isPlaying
|
||||
? controller.pausePlayback()
|
||||
: controller.resumePlayback();
|
||||
},
|
||||
iconSize: 56,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
IconButton(
|
||||
icon:
|
||||
const Icon(Icons.delete_outline, color: Colors.redAccent),
|
||||
tooltip: 'Delete'.tr,
|
||||
onPressed: () {
|
||||
_showDeleteConfirmation(context, controller,
|
||||
isDeleteAll: false);
|
||||
},
|
||||
iconSize: 28,
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Shows a confirmation dialog for deleting one or all files.
|
||||
void _showDeleteConfirmation(
|
||||
BuildContext context,
|
||||
AudioRecorderController controller, {
|
||||
required bool isDeleteAll,
|
||||
}) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(isDeleteAll
|
||||
? 'Delete All Recordings?'.tr
|
||||
: 'Delete Recording?'.tr),
|
||||
content: Text(isDeleteAll
|
||||
? 'This action cannot be undone.'.tr
|
||||
: 'Are you sure you want to delete this file?'.tr),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text('Cancel'.tr),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
TextButton(
|
||||
child:
|
||||
Text('Delete'.tr, style: const TextStyle(color: Colors.red)),
|
||||
onPressed: () async {
|
||||
if (isDeleteAll) {
|
||||
await controller.deleteAllRecordedFiles();
|
||||
} else {
|
||||
// NOTE: You may need to add this method to your controller
|
||||
// if it doesn't exist.
|
||||
// await controller.deleteFile(controller.selectedFilePath!);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user