Files
intaleq/lib/views/home/map_widget.dart/main_bottom_Menu_map.dart

2088 lines
79 KiB
Dart

import 'package:Intaleq/print.dart';
import 'package:Intaleq/views/widgets/my_textField.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Intaleq/constant/box_name.dart';
import 'package:Intaleq/constant/style.dart';
import 'package:Intaleq/controller/home/map_passenger_controller.dart';
import 'package:Intaleq/main.dart';
import 'package:Intaleq/views/home/map_widget.dart/form_search_places_destenation.dart';
import 'package:Intaleq/views/widgets/elevated_btn.dart';
import 'package:maplibre_gl/maplibre_gl.dart';
import '../../../constant/colors.dart';
import '../../../constant/table_names.dart';
import '../../../controller/functions/toast.dart';
import '../../../controller/functions/tts.dart';
import '../../widgets/error_snakbar.dart';
import '../../widgets/mydialoug.dart';
import 'form_search_start.dart';
// ─── Design Tokens (Modern & Dynamic) ────────────────────────────────────────
class _D {
// Radii - More rounded for modern feel
static const double radiusCard = 28;
static const double radiusChip = 20;
static const double radiusBtn = 16;
static const double radiusInner = 14;
static const double radiusPill = 50;
// Shadows - Layered depth with blur
static List<BoxShadow> get cardShadow => [
BoxShadow(
color: Colors.black.withOpacity(0.08),
blurRadius: 40,
spreadRadius: -8,
offset: const Offset(0, 12),
),
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 16,
spreadRadius: -4,
offset: const Offset(0, 4),
),
];
static List<BoxShadow> glowShadow(Color c, {double intensity = 0.4}) => [
BoxShadow(
color: c.withOpacity(intensity),
blurRadius: 24,
spreadRadius: -4,
offset: const Offset(0, 8),
),
BoxShadow(
color: c.withOpacity(intensity * 0.5),
blurRadius: 12,
spreadRadius: -2,
offset: const Offset(0, 3),
),
];
static List<BoxShadow> innerGlow(Color c) => [
BoxShadow(
color: c.withOpacity(0.15),
blurRadius: 20,
spreadRadius: -10,
offset: const Offset(0, 0),
),
];
// Durations - Smoother animations
static const Duration fast = Duration(milliseconds: 180);
static const Duration medium = Duration(milliseconds: 420);
static const Duration slow = Duration(milliseconds: 600);
// Gradients
static LinearGradient primaryGradient({
Alignment begin = Alignment.topLeft,
Alignment end = Alignment.bottomRight,
}) =>
LinearGradient(
begin: begin,
end: end,
colors: [
AppColor.primaryColor,
AppColor.primaryColor.withOpacity(0.85),
AppColor.primaryColor.withOpacity(0.7),
],
stops: const [0.0, 0.5, 1.0],
);
static LinearGradient cardGradient() => LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
AppColor.secondaryColor.withOpacity(0.98),
AppColor.secondaryColor
.withBlue(
(AppColor.secondaryColor.blue + 12).clamp(0, 255),
)
.withOpacity(0.95),
],
);
// Glassmorphism effect
static BoxDecoration glassEffect({
required Color color,
double opacity = 0.1,
double borderOpacity = 0.2,
}) =>
BoxDecoration(
color: color.withOpacity(opacity),
borderRadius: BorderRadius.circular(radiusInner),
border: Border.all(
color: color.withOpacity(borderOpacity),
width: 1,
),
backgroundBlendMode: BlendMode.overlay,
);
}
// ─────────────────────────────────────────────────────────────────────────────
// MAIN BOTTOM MENU MAP - Modern Redesign
// ─────────────────────────────────────────────────────────────────────────────
class MainBottomMenuMap extends StatelessWidget {
const MainBottomMenuMap({super.key});
@override
Widget build(BuildContext context) {
Get.put(MapPassengerController());
return GetBuilder<MapPassengerController>(
builder: (controller) {
if (controller.isPickerShown) {
return _MapPickerOverlay(controller: controller);
}
return Positioned(
bottom: Get.height * .035,
left: 16,
right: 16,
child: AnimatedContainer(
duration: _D.medium,
curve: Curves.easeOutQuint,
height: controller.mainBottomMenuMapHeight,
decoration: BoxDecoration(
gradient: _D.cardGradient(),
borderRadius: BorderRadius.circular(_D.radiusCard),
boxShadow: _D.cardShadow,
border: Border.all(
color: Get.isDarkMode
? Colors.white.withOpacity(0.15)
: Colors.white.withOpacity(0.65),
width: 1.2,
),
),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(_D.radiusCard),
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.white.withOpacity(0.08),
Colors.transparent,
],
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(_D.radiusCard),
child: SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(),
child: controller.isMainBottomMenuMap
? _CollapsedView(controller: controller)
: _ExpandedView(controller: controller, context: context),
),
),
),
),
);
},
);
}
}
// ─────────────────────────────────────────────────────────────────────────────
// COLLAPSED VIEW - Modern & Elegant
// ─────────────────────────────────────────────────────────────────────────────
class _CollapsedView extends StatelessWidget {
final MapPassengerController controller;
const _CollapsedView({required this.controller});
@override
Widget build(BuildContext context) {
final String firstName = box.read(BoxName.name).toString().split(' ').first;
return Column(
mainAxisSize: MainAxisSize.min,
children: [
// ── Animated drag handle ─────────────────────────────────────────────
const SizedBox(height: 14),
AnimatedContainer(
duration: _D.fast,
width: 44,
height: 5,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.grey.shade400.withOpacity(0.6),
Colors.grey.shade300,
Colors.grey.shade400.withOpacity(0.6),
],
),
borderRadius: BorderRadius.circular(3),
),
),
const SizedBox(height: 16),
// ── Main interactive search card ─────────────────────────────────────
Semantics(
button: true,
label: 'Open destination search'.tr,
hint: 'Double tap to open search or enter destination'.tr,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: controller.changeMainBottomMenuMap,
borderRadius: BorderRadius.circular(_D.radiusInner),
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 18, vertical: 8),
child: Row(
children: [
// Animated search icon with glow
AnimatedContainer(
duration: _D.medium,
width: 48,
height: 48,
decoration: BoxDecoration(
gradient: _D.primaryGradient(),
borderRadius: BorderRadius.circular(_D.radiusPill),
boxShadow: _D.glowShadow(AppColor.primaryColor),
),
child: const Icon(
Icons.search_rounded,
color: Colors.white,
size: 22,
),
),
const SizedBox(width: 16),
// Dynamic text content
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text.rich(
TextSpan(
children: [
TextSpan(
text: '${'Where to'.tr} ',
style: AppStyle.title.copyWith(
fontWeight: FontWeight.w600,
fontSize: 16,
color: Colors.grey.shade700,
),
),
TextSpan(
text: firstName,
style: AppStyle.title.copyWith(
fontWeight: FontWeight.w800,
fontSize: 16.5,
color: AppColor.primaryColor,
letterSpacing: -0.3,
),
),
const TextSpan(text: '؟'),
],
),
),
const SizedBox(height: 2),
if (!controller.noCarString)
AnimatedOpacity(
duration: _D.fast,
opacity: 1,
child: Text(
'Tap to search your destination'.tr,
style: AppStyle.subtitle.copyWith(
fontSize: 12,
color: Colors.grey.shade500,
fontWeight: FontWeight.w400,
),
),
),
],
),
),
// Elegant expand indicator
Container(
padding: const EdgeInsets.symmetric(
horizontal: 14, vertical: 8),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColor.primaryColor.withOpacity(0.12),
AppColor.primaryColor.withOpacity(0.06),
],
),
borderRadius: BorderRadius.circular(_D.radiusPill),
border: Border.all(
color: AppColor.primaryColor.withOpacity(0.25),
width: 1,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedRotation(
duration: _D.fast,
turns: 0,
child: Icon(
Icons.keyboard_arrow_up_rounded,
color: AppColor.primaryColor,
size: 20,
),
),
const SizedBox(width: 4),
Text(
'Open'.tr,
style: TextStyle(
color: AppColor.primaryColor,
fontSize: 12.5,
fontWeight: FontWeight.w700,
letterSpacing: 0.3,
),
),
],
),
),
],
),
),
),
),
),
// ── Recent places - Modern horizontal chips ───────────────────────────
if (controller.recentPlaces.isNotEmpty) ...[
const SizedBox(height: 12),
Container(
height: 40,
padding: const EdgeInsets.symmetric(horizontal: 18),
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: controller.recentPlaces.length,
separatorBuilder: (_, __) => const SizedBox(width: 10),
itemBuilder: (context, index) =>
_RecentPlaceChip(controller: controller, index: index),
),
),
const SizedBox(height: 16),
] else
const SizedBox(height: 20),
],
);
}
}
// ─────────────────────────────────────────────────────────────────────────────
// EXPANDED VIEW - Dynamic Route Planner
// ─────────────────────────────────────────────────────────────────────────────
class _ExpandedView extends StatelessWidget {
final MapPassengerController controller;
final BuildContext context;
const _ExpandedView({required this.controller, required this.context});
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// ── Animated drag handle ─────────────────────────────────────────────
const SizedBox(height: 14),
Center(
child: AnimatedContainer(
duration: _D.fast,
width: 44,
height: 5,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.grey.shade400.withOpacity(0.6),
Colors.grey.shade300,
Colors.grey.shade400.withOpacity(0.6),
],
),
borderRadius: BorderRadius.circular(3),
),
),
),
// ── Modern Header with gradient ──────────────────────────────────────
Container(
padding: const EdgeInsets.fromLTRB(20, 18, 16, 14),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.transparent,
Colors.grey.shade50.withOpacity(0.3),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
child: Row(
children: [
// Animated icon container
AnimatedContainer(
duration: _D.medium,
width: 42,
height: 42,
decoration: BoxDecoration(
gradient: _D.primaryGradient(),
borderRadius: BorderRadius.circular(_D.radiusInner),
boxShadow: _D.glowShadow(AppColor.primaryColor),
),
child: const Icon(
Icons.alt_route_rounded,
color: Colors.white,
size: 20,
),
),
const SizedBox(width: 14),
Text(
'Plan Your Route'.tr,
style: AppStyle.title.copyWith(
fontWeight: FontWeight.w800,
fontSize: 18,
letterSpacing: -0.5,
height: 1.2,
),
),
const Spacer(),
// Elegant close button
Semantics(
button: true,
label: 'Close panel'.tr,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: controller.changeMainBottomMenuMap,
borderRadius: BorderRadius.circular(_D.radiusPill),
child: AnimatedContainer(
duration: _D.fast,
width: 38,
height: 38,
decoration: BoxDecoration(
color: Colors.grey.shade100,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.grey.shade200.withOpacity(0.5),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Icon(
Icons.keyboard_arrow_down_rounded,
size: 24,
color: Colors.grey.shade600,
),
),
),
),
),
],
),
),
// Subtle separator with gradient
Container(
height: 1,
margin: const EdgeInsets.symmetric(horizontal: 20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.transparent,
Colors.grey.shade200,
Colors.transparent,
],
),
),
),
const SizedBox(height: 16),
// ── Dynamic Route Timeline ───────────────────────────────────────────
// Start location row
_buildTimelineItem(
dotColor: AppColor.primaryColor,
showTopLine: false,
showBottomLine: true,
isStart: true,
child: !controller.isAnotherOreder
? _TimelineRow(
icon: Icons.my_location_rounded,
iconColor: AppColor.primaryColor,
bgColor: AppColor.primaryColor,
label: controller.currentLocationString,
)
: Padding(
padding: const EdgeInsets.only(right: 16),
child: formSearchPlacesStart(),
),
),
// Dynamic waypoints with color coding
...List.generate(controller.activeMenuWaypointCount, (index) {
final wpName = controller.menuWaypointNames[index];
final isSet = controller.menuWaypoints[index] != null;
final Color accent =
index == 0 ? Colors.amber.shade600 : Colors.deepPurple.shade400;
final Color soft =
index == 0 ? Colors.amber.shade50 : Colors.deepPurple.shade50;
return _buildTimelineItem(
dotColor: accent,
showTopLine: true,
showBottomLine: true,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
soft.withOpacity(0.9),
soft.withOpacity(0.6),
],
),
borderRadius: BorderRadius.circular(_D.radiusInner),
border: Border.all(
color:
isSet ? accent.withOpacity(0.35) : Colors.grey.shade200,
width: 1,
),
boxShadow: isSet
? [
BoxShadow(
color: accent.withOpacity(0.08),
blurRadius: 12,
offset: const Offset(0, 3),
)
]
: null,
),
child: Row(
children: [
// Animated waypoint number badge
AnimatedContainer(
duration: _D.fast,
width: 26,
height: 26,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: index == 0
? [Colors.amber.shade400, Colors.amber.shade700]
: [
Colors.deepPurple.shade300,
Colors.deepPurple.shade500
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: accent.withOpacity(0.35),
blurRadius: 8,
offset: const Offset(0, 3),
),
],
),
child: Center(
child: Text(
'${index + 1}',
style: const TextStyle(
color: Colors.white,
fontSize: 11,
fontWeight: FontWeight.w800,
letterSpacing: -0.3,
),
),
),
),
const SizedBox(width: 12),
Expanded(
child: Semantics(
button: true,
label: isSet
? '${'Waypoint'.tr} $wpName'
: '${'Stop'.tr} ${index + 1}',
hint: 'Double tap to set or change this waypoint on the map'
.tr,
child: GestureDetector(
onTap: () {
controller.changeMainBottomMenuMap();
controller.startPickingWaypointOnMap(index);
},
child: Text(
isSet ? wpName : '${'Stop'.tr} ${index + 1}',
style: TextStyle(
fontSize: 13.5,
color: isSet
? accent.withOpacity(0.9)
: Colors.grey.shade400,
fontWeight: isSet ? FontWeight.w600 : FontWeight.w400,
fontStyle:
isSet ? FontStyle.normal : FontStyle.italic,
height: 1.3,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
)),
// Map button with hover effect simulation
Semantics(
button: true,
label: 'Pick location on map'.tr,
child: GestureDetector(
onTap: () {
controller.changeMainBottomMenuMap();
controller.startPickingWaypointOnMap(index);
},
child: AnimatedContainer(
duration: _D.fast,
width: 34,
height: 34,
margin: const EdgeInsets.symmetric(horizontal: 4),
decoration: BoxDecoration(
color: accent.withOpacity(0.12),
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: accent.withOpacity(0.25),
width: 1,
),
),
child: Icon(
Icons.map_outlined,
color: accent,
size: 17,
),
),
),
),
// Remove button with subtle animation
Semantics(
button: true,
label: 'Remove waypoint'.tr,
child: GestureDetector(
onTap: () => controller.removeMenuWaypoint(index),
child: AnimatedContainer(
duration: _D.fast,
width: 28,
height: 28,
decoration: BoxDecoration(
color: Colors.red.shade50,
shape: BoxShape.circle,
border: Border.all(
color: Colors.red.shade100,
width: 1,
),
),
child: Icon(
Icons.close_rounded,
color: Colors.red.shade400,
size: 15,
),
),
),
),
],
),
),
);
}),
// Add stop button - Modern CTA style
if (controller.activeMenuWaypointCount < 2)
_buildTimelineItem(
dotColor: Colors.orange.shade300,
isDotDashed: true,
showTopLine: true,
showBottomLine: true,
child: Semantics(
button: true,
label: 'Add a new waypoint stop'.tr,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () => controller.addMenuWaypoint(),
borderRadius: BorderRadius.circular(_D.radiusInner),
child: AnimatedContainer(
duration: _D.fast,
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(_D.radiusInner),
border: Border.all(
color: Colors.orange.shade200,
style: BorderStyle.solid,
width: 1.5,
),
gradient: LinearGradient(
colors: [
Colors.orange.shade50.withOpacity(0.6),
Colors.orange.shade50.withOpacity(0.3),
],
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.add_location_alt_outlined,
color: Colors.orange.shade500,
size: 18,
),
const SizedBox(width: 10),
Text(
'Add a Stop'.tr,
style: TextStyle(
color: Colors.orange.shade700,
fontSize: 13.5,
fontWeight: FontWeight.w600,
letterSpacing: 0.2,
),
),
const SizedBox(width: 10),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: Colors.orange.shade100,
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: Colors.orange.shade200,
width: 1,
),
),
child: Text(
'+5 ${'min'.tr}',
style: TextStyle(
color: Colors.orange.shade700,
fontSize: 10.5,
fontWeight: FontWeight.w700,
),
),
),
],
),
),
),
),
),
),
// Destination row with elegant styling
_buildTimelineItem(
dotColor: Colors.red.shade500,
showTopLine: true,
showBottomLine: false,
isEnd: true,
child: Padding(
padding: const EdgeInsets.only(right: 16),
child: formSearchPlacesDestenation(),
),
),
// ── Smart Surcharge banner ───────────────────────────────────────────
if (controller.activeMenuWaypointCount > 0)
AnimatedContainer(
duration: _D.medium,
margin: const EdgeInsets.fromLTRB(20, 8, 20, 0),
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.orange.shade50.withOpacity(0.95),
Colors.amber.shade50.withOpacity(0.85),
],
),
borderRadius: BorderRadius.circular(_D.radiusInner),
border: Border.all(
color: Colors.orange.shade100.withOpacity(0.8),
width: 1,
),
boxShadow: [
BoxShadow(
color: Colors.orange.shade100.withOpacity(0.3),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: Row(
children: [
AnimatedContainer(
duration: _D.fast,
width: 30,
height: 30,
decoration: BoxDecoration(
color: Colors.orange.shade100,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.orange.shade200.withOpacity(0.4),
blurRadius: 6,
offset: const Offset(0, 2),
),
],
),
child: Icon(
Icons.schedule_rounded,
size: 15,
color: Colors.orange.shade700,
),
),
const SizedBox(width: 12),
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: '${controller.activeMenuWaypointCount} ',
style: TextStyle(
fontSize: 13,
color: Colors.orange.shade800,
fontWeight: FontWeight.w700,
),
),
TextSpan(
text: '${'stop(s)'.tr} · +',
style: TextStyle(
fontSize: 12.5,
color: Colors.orange.shade600,
fontWeight: FontWeight.w500,
),
),
TextSpan(
text: '${controller.activeMenuWaypointCount * 5} ',
style: TextStyle(
fontSize: 13,
color: Colors.orange.shade800,
fontWeight: FontWeight.w700,
),
),
TextSpan(
text: 'min added to fare'.tr,
style: TextStyle(
fontSize: 12.5,
color: Colors.orange.shade600,
fontWeight: FontWeight.w500,
),
),
],
),
),
),
],
),
),
const SizedBox(height: 14),
// ── WhatsApp button - Modern card style ──────────────────────────────
Padding(
padding: const EdgeInsets.symmetric(horizontal: 18),
child: _WhatsAppLinkButton(controller: controller),
),
const SizedBox(height: 12),
// ── Order type button - Dynamic toggle style ─────────────────────────
Padding(
padding: const EdgeInsets.symmetric(horizontal: 18),
child: _OrderTypeButton(controller: controller),
),
const SizedBox(height: 18),
],
);
}
Widget _buildTimelineItem({
required Color dotColor,
required bool showTopLine,
required bool showBottomLine,
required Widget child,
bool isDotDashed = false,
bool isStart = false,
bool isEnd = false,
}) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 24,
child: Column(
children: [
if (showTopLine)
AnimatedContainer(
duration: _D.fast,
width: 2.5,
height: 12,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.grey.shade200.withOpacity(0.4),
Colors.grey.shade300,
],
),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(2),
),
),
),
// Animated dot with pulse effect simulation
AnimatedContainer(
duration: _D.medium,
width: isDotDashed ? 15 : 15,
height: isDotDashed ? 15 : 15,
decoration: BoxDecoration(
color: isDotDashed
? Colors.transparent
: dotColor.withOpacity(0.95),
shape: BoxShape.circle,
border: Border.all(
color: isDotDashed ? dotColor : dotColor.withOpacity(0.3),
width: isDotDashed ? 2 : 3,
style:
isDotDashed ? BorderStyle.solid : BorderStyle.solid,
),
boxShadow: isDotDashed
? []
: [
BoxShadow(
color: dotColor.withOpacity(0.35),
blurRadius: 10,
spreadRadius: 2,
),
BoxShadow(
color: dotColor.withOpacity(0.15),
blurRadius: 20,
spreadRadius: -5,
),
],
),
child: isDotDashed
? Center(
child: Container(
width: 5,
height: 5,
decoration: BoxDecoration(
color: dotColor,
shape: BoxShape.circle,
),
),
)
: null,
),
if (showBottomLine)
AnimatedContainer(
duration: _D.fast,
width: 2.5,
height: 12,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.grey.shade300,
Colors.grey.shade200.withOpacity(0.4),
],
),
borderRadius: const BorderRadius.vertical(
bottom: Radius.circular(2),
),
),
),
],
),
),
const SizedBox(width: 14),
Expanded(child: child),
],
),
);
}
}
// ─────────────────────────────────────────────────────────────────────────────
// MAP PICKER OVERLAY - Glassmorphism Design
// ─────────────────────────────────────────────────────────────────────────────
class _MapPickerOverlay extends StatelessWidget {
final MapPassengerController controller;
const _MapPickerOverlay({required this.controller});
String _getModeTitle(BuildContext context) {
if (controller.isPickingWaypoint) {
return 'Move map to set stop'.tr +
' ${controller.pickingWaypointIndex + 1}'.tr;
}
if (controller.passengerStartLocationFromMap) {
return controller.isAnotherOreder
? 'Now set the pickup point for the other person'.tr
: 'Move map to your pickup point'.tr;
} else if (controller.startLocationFromMap) {
return 'Move map to set start location'.tr;
} else if (controller.workLocationFromMap) {
return 'Move map to your work location'.tr;
} else if (controller.homeLocationFromMap) {
return 'Move map to your home location'.tr;
}
return 'Move map to select destination'.tr;
}
String _getConfirmLabel(BuildContext context) {
if (controller.isPickingWaypoint) return 'Set as Stop'.tr;
if (controller.passengerStartLocationFromMap)
return 'Confirm Pickup Location'.tr;
if (controller.workLocationFromMap) return 'Set as Work'.tr;
if (controller.homeLocationFromMap) return 'Set as Home'.tr;
return 'Set Destination'.tr;
}
IconData _getModeIcon() {
if (controller.isPickingWaypoint) return Icons.add_location_alt_rounded;
if (controller.passengerStartLocationFromMap)
return Icons.person_pin_circle_rounded;
if (controller.workLocationFromMap) return Icons.work_rounded;
if (controller.homeLocationFromMap) return Icons.home_rounded;
return Icons.location_on_rounded;
}
Color _getModeColor() {
if (controller.isPickingWaypoint) return Colors.orange.shade600;
if (controller.passengerStartLocationFromMap) return Colors.green.shade600;
if (controller.workLocationFromMap) return Colors.blue.shade600;
if (controller.homeLocationFromMap) return Colors.orange.shade600;
return AppColor.primaryColor;
}
@override
Widget build(BuildContext context) {
final modeColor = _getModeColor();
return Positioned(
bottom: Get.height * .035,
left: 16,
right: 16,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// ── Dynamic instruction banner with gradient ───────────────────────
AnimatedContainer(
duration: _D.medium,
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 14),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
modeColor,
modeColor.withOpacity(0.88),
modeColor.withOpacity(0.75),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(_D.radiusCard),
boxShadow: _D.glowShadow(modeColor, intensity: 0.5),
border: Border.all(
color: Get.isDarkMode
? Colors.white.withOpacity(0.15)
: Colors.white.withOpacity(0.35),
width: 1,
),
),
child: Row(
children: [
AnimatedContainer(
duration: _D.fast,
width: 38,
height: 38,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.22),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Colors.white.withOpacity(0.3),
width: 1,
),
),
child: Icon(
_getModeIcon(),
color: Colors.white,
size: 19,
),
),
const SizedBox(width: 14),
Expanded(
child: Text(
_getModeTitle(context),
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700,
fontSize: 14,
height: 1.3,
letterSpacing: -0.2,
),
),
),
],
),
),
const SizedBox(height: 12),
// ── Glassmorphism coordinate card ─────────────────────────────────
Container(
decoration: BoxDecoration(
color: AppColor.secondaryColor.withOpacity(0.98),
borderRadius: BorderRadius.circular(_D.radiusCard),
boxShadow: _D.cardShadow,
border: Border.all(
color: Get.isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.white.withOpacity(0.7),
width: 1.3,
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(_D.radiusCard),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Coordinate display with modern styling
Padding(
padding: const EdgeInsets.fromLTRB(20, 18, 20, 2),
child: Row(
children: [
AnimatedContainer(
duration: _D.fast,
width: 34,
height: 34,
decoration: BoxDecoration(
color: modeColor.withOpacity(0.12),
borderRadius: BorderRadius.circular(11),
border: Border.all(
color: modeColor.withOpacity(0.25),
width: 1,
),
),
child: Icon(
Icons.gps_fixed_rounded,
color: modeColor,
size: 16,
),
),
const SizedBox(width: 14),
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: Colors.grey.shade50,
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: Colors.grey.shade200,
width: 1,
),
),
child: Text(
'${controller.newMyLocation.latitude.toStringAsFixed(5)}, '
'${controller.newMyLocation.longitude.toStringAsFixed(5)}',
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade700,
fontFeatures: const [
FontFeature.tabularFigures()
],
letterSpacing: 0.3,
fontWeight: FontWeight.w500,
),
),
),
),
const SizedBox(width: 10),
// Live indicator badge
Container(
padding: const EdgeInsets.symmetric(
horizontal: 9, vertical: 4),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.green.shade100,
Colors.green.shade50,
],
),
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: Colors.green.shade200,
width: 1,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedContainer(
duration: const Duration(milliseconds: 500),
width: 7,
height: 7,
decoration: BoxDecoration(
color: Colors.green.shade500,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.green.shade300
.withOpacity(0.6),
blurRadius: 6,
spreadRadius: 1,
),
],
),
),
const SizedBox(width: 5),
Text(
'Live',
style: TextStyle(
fontSize: 10.5,
color: Colors.green.shade700,
fontWeight: FontWeight.w700,
letterSpacing: 0.5,
),
),
],
),
),
],
),
),
const SizedBox(height: 16),
// Elegant separator
Container(
height: 1,
margin: const EdgeInsets.symmetric(horizontal: 20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.transparent,
Colors.grey.shade200,
Colors.transparent,
],
),
),
),
// Action buttons with modern styling
Padding(
padding: const EdgeInsets.fromLTRB(16, 14, 16, 18),
child: Row(
children: [
// Cancel button - Subtle outline style
Expanded(
flex: 2,
child: AnimatedContainer(
duration: _D.fast,
child: OutlinedButton.icon(
onPressed: () {
controller.isPickerShown = false;
controller.passengerStartLocationFromMap =
false;
controller.startLocationFromMap = false;
controller.workLocationFromMap = false;
controller.homeLocationFromMap = false;
controller.isPickingWaypoint = false;
controller.pickingWaypointIndex = -1;
if (!controller.isMainBottomMenuMap) {
controller.isMainBottomMenuMap = true;
controller.mainBottomMenuMapHeight =
Get.height * .22;
}
controller.update();
},
style: OutlinedButton.styleFrom(
foregroundColor: Colors.grey.shade600,
side: BorderSide(
color: Colors.grey.shade200, width: 1.5),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(_D.radiusBtn),
),
padding:
const EdgeInsets.symmetric(vertical: 14),
elevation: 0,
),
icon: const Icon(Icons.close_rounded, size: 17),
label: Text(
'Cancel'.tr,
style: const TextStyle(
fontSize: 13.5,
fontWeight: FontWeight.w600),
),
),
),
),
const SizedBox(width: 12),
// Confirm button - Gradient with glow
Expanded(
flex: 3,
child: AnimatedContainer(
duration: _D.fast,
child: ElevatedButton.icon(
onPressed: () =>
_onConfirmTap(controller, context),
style: ElevatedButton.styleFrom(
backgroundColor: modeColor,
foregroundColor: Colors.white,
elevation: 0,
shadowColor: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(_D.radiusBtn),
),
padding:
const EdgeInsets.symmetric(vertical: 14),
),
icon: Icon(_getModeIcon(), size: 18),
label: Text(
_getConfirmLabel(context),
style: const TextStyle(
fontWeight: FontWeight.w700,
fontSize: 13.5,
letterSpacing: 0.2),
),
),
),
),
],
),
),
],
),
),
),
],
),
);
}
// ── confirm logic (unchanged) ─────────────────────────────────────────────
Future<void> _onConfirmTap(
MapPassengerController controller, BuildContext context) async {
await Future.delayed(const Duration(milliseconds: 280));
final LatLng currentCameraPosition = LatLng(
controller.newMyLocation.latitude,
controller.newMyLocation.longitude,
);
Log.print(
'🌐 MAP PICKER CENTER: ${currentCameraPosition.latitude}, ${currentCameraPosition.longitude}');
Log.print(
'✅ _onConfirmTap confirmed coordinates: ${currentCameraPosition.latitude}, ${currentCameraPosition.longitude}');
if (controller.isPickingWaypoint && controller.pickingWaypointIndex >= 0) {
final int wpIndex = controller.pickingWaypointIndex;
controller.setMenuWaypointFromMap(wpIndex, currentCameraPosition);
mySnackbarSuccess('Waypoint has been set successfully'.tr);
return;
}
controller.clearPolyline();
controller.data = [];
if (controller.passengerStartLocationFromMap) {
final LatLng start = currentCameraPosition;
controller.newStartPointLocation = start;
controller.passengerStartLocationFromMap = false;
controller.isPickerShown = false;
controller.currentLocationToFormPlaces = false;
controller.placesDestination = [];
controller.clearPlacesStart();
controller.clearPlacesDestination();
controller.isMainBottomMenuMap = true;
controller.mainBottomMenuMapHeight = Get.height * .22;
controller.update();
await controller.getDirectionMap(
'${start.latitude},${start.longitude}',
'${controller.myDestination.latitude},${controller.myDestination.longitude}',
);
controller.showBottomSheet1();
return;
}
if (controller.startLocationFromMap) {
final LatLng start = currentCameraPosition;
controller.newMyLocation = start;
controller.newStartPointLocation = start;
controller.hintTextStartPoint =
'${start.latitude.toStringAsFixed(4)} , ${start.longitude.toStringAsFixed(4)}';
controller.startLocationFromMap = false;
controller.isPickerShown = false;
controller.update();
return;
}
if (controller.workLocationFromMap) {
final LatLng work = currentCameraPosition;
box.write(BoxName.addWork,
'${work.latitude.toStringAsFixed(4)} , ${work.longitude.toStringAsFixed(4)}');
controller.hintTextDestinationPoint = 'To Work'.tr;
controller.workLocationFromMap = false;
controller.isPickerShown = false;
controller.update();
mySnackbarSuccess('Work Saved'.tr);
return;
}
if (controller.homeLocationFromMap) {
final LatLng home = currentCameraPosition;
box.write(BoxName.addHome,
'${home.latitude.toStringAsFixed(4)} , ${home.longitude.toStringAsFixed(4)}');
controller.hintTextDestinationPoint = 'To Home'.tr;
controller.homeLocationFromMap = false;
controller.isPickerShown = false;
controller.update();
mySnackbarSuccess('Home Saved'.tr);
return;
}
final LatLng confirmedDestination = currentCameraPosition;
controller.myDestination = confirmedDestination;
controller.hintTextDestinationPoint =
'${confirmedDestination.latitude.toStringAsFixed(4)} , ${confirmedDestination.longitude.toStringAsFixed(4)}';
controller.placesDestination = [];
controller.placeDestinationController.clear();
controller.passengerStartLocationFromMap = true;
controller.isPickerShown = true; // ✅ Keep picker UI open for pickup selection
controller.update();
try {
if (controller.isAnotherOreder) {
await controller.mapController?.animateCamera(
CameraUpdate.newLatLng(LatLng(
controller.newStartPointLocation.latitude,
controller.newStartPointLocation.longitude,
)),
);
} else {
await controller.mapController?.animateCamera(
CameraUpdate.newLatLng(LatLng(
controller.passengerLocation.latitude,
controller.passengerLocation.longitude,
)),
);
}
} catch (e) {
Log.print("Error occurred: $e");
}
Get.snackbar(
'Destination Set'.tr,
controller.isAnotherOreder
? 'Now set the pickup point for the other person'.tr
: 'Now move the map to your pickup point'.tr,
backgroundColor: Colors.green.shade600,
colorText: Colors.white,
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 2),
margin: const EdgeInsets.all(12),
borderRadius: 12,
);
}
}
// ─────────────────────────────────────────────────────────────────────────────
// HELPER WIDGETS - Modern Redesign
// ─────────────────────────────────────────────────────────────────────────────
class _LocationRow extends StatelessWidget {
final IconData icon;
final Color iconColor;
final String label;
final bool isStart;
const _LocationRow({
required this.icon,
required this.iconColor,
required this.label,
this.isStart = false,
});
@override
Widget build(BuildContext context) {
return AnimatedContainer(
duration: _D.fast,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
gradient: isStart
? LinearGradient(
colors: [
AppColor.primaryColor.withOpacity(0.06),
AppColor.primaryColor.withOpacity(0.02),
],
)
: null,
color: isStart ? null : Colors.transparent,
borderRadius: BorderRadius.circular(_D.radiusInner),
border: Border.all(color: Colors.grey.shade100, width: 1),
),
child: Row(
children: [
AnimatedContainer(
duration: _D.fast,
width: 32,
height: 32,
decoration: BoxDecoration(
color: iconColor.withOpacity(0.12),
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: iconColor.withOpacity(0.2),
width: 1,
),
),
child: Icon(icon, color: iconColor, size: 16),
),
const SizedBox(width: 12),
Expanded(
child: Text(
label,
style: AppStyle.subtitle.copyWith(
fontSize: 13.5,
fontWeight: FontWeight.w500,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
);
}
}
class _TimelineRow extends StatelessWidget {
final IconData icon;
final Color iconColor;
final Color bgColor;
final String label;
const _TimelineRow({
required this.icon,
required this.iconColor,
required this.bgColor,
required this.label,
});
@override
Widget build(BuildContext context) {
return AnimatedContainer(
duration: _D.fast,
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 11),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
bgColor.withAlpha(15),
bgColor.withAlpha(8),
],
),
borderRadius: BorderRadius.circular(_D.radiusInner),
border: Border.all(
color: bgColor.withAlpha(35),
width: 1,
),
),
child: Row(
children: [
AnimatedContainer(
duration: _D.fast,
width: 30,
height: 30,
decoration: BoxDecoration(
color: bgColor.withAlpha(25),
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: bgColor.withAlpha(40),
width: 1,
),
),
child: Icon(icon, color: iconColor, size: 15),
),
const SizedBox(width: 12),
Expanded(
child: Text(
label,
style: AppStyle.subtitle.copyWith(
fontSize: 13,
fontWeight: FontWeight.w500,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
);
}
}
/// شريحة الأماكن الأخيرة - تصميم عصري
class _RecentPlaceChip extends StatelessWidget {
final MapPassengerController controller;
final int index;
const _RecentPlaceChip({required this.controller, required this.index});
@override
Widget build(BuildContext context) {
final place = controller.recentPlaces[index];
return Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
MyDialog().getDialog(
'Are you want to go this site'.tr,
' ',
() async {
Get.back();
await controller.getLocation();
await controller.getDirectionMap(
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
'${place['latitude']},${place['longitude']}',
);
controller.showBottomSheet1();
},
);
},
onLongPress: () {
MyDialog().getDialog(
'Are you sure to delete this location?'.tr,
'',
() {
sql.deleteData(TableName.recentLocations, place['id']);
controller.getFavioratePlaces();
controller.update();
Get.back();
mySnackbarSuccess('deleted'.tr);
},
);
},
borderRadius: BorderRadius.circular(_D.radiusChip),
child: AnimatedContainer(
duration: _D.fast,
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 7),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColor.primaryColor.withOpacity(0.08),
AppColor.primaryColor.withOpacity(0.04),
],
),
borderRadius: BorderRadius.circular(_D.radiusChip),
border: Border.all(
color: AppColor.primaryColor.withOpacity(0.18),
width: 1,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.history_rounded,
size: 14,
color: AppColor.primaryColor.withOpacity(0.7),
),
const SizedBox(width: 7),
Text(
place['name'] ?? '',
style: TextStyle(
fontSize: 12.5,
color: AppColor.primaryColor.withOpacity(0.9),
fontWeight: FontWeight.w600,
letterSpacing: -0.2,
),
),
],
),
),
),
);
}
}
/// زر رابط الواتساب - تصميم بطاقات حديث
class _WhatsAppLinkButton extends StatelessWidget {
final MapPassengerController controller;
const _WhatsAppLinkButton({required this.controller});
@override
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
Get.dialog(
AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(22)),
title: Text('WhatsApp Location Extractor'.tr),
content: Form(
key: controller.sosFormKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
MyTextForm(
controller: controller.whatsAppLocationText,
label: 'Location Link'.tr,
hint: 'Paste location link here'.tr,
type: TextInputType.url,
),
const SizedBox(height: 16),
MyElevatedButton(
title: 'Go to this location'.tr,
onPressed: () => controller.goToWhatappLocation(),
),
],
),
),
),
);
},
borderRadius: BorderRadius.circular(_D.radiusInner),
child: AnimatedContainer(
duration: _D.fast,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.green.shade50.withOpacity(0.9),
Colors.green.shade50.withOpacity(0.6),
],
),
borderRadius: BorderRadius.circular(_D.radiusInner),
border: Border.all(
color: Colors.green.shade100.withOpacity(0.9),
width: 1,
),
),
child: Row(
children: [
AnimatedContainer(
duration: _D.fast,
width: 34,
height: 34,
decoration: BoxDecoration(
color: Colors.green.shade100,
borderRadius: BorderRadius.circular(11),
border: Border.all(
color: Colors.green.shade200,
width: 1,
),
),
child: Icon(
Icons.link_rounded,
color: Colors.green.shade700,
size: 18,
),
),
const SizedBox(width: 14),
Expanded(
child: Text(
'Paste WhatsApp location link'.tr,
style: TextStyle(
color: Colors.green.shade800,
fontSize: 13.5,
fontWeight: FontWeight.w600,
),
),
),
Icon(
Icons.arrow_forward_ios_rounded,
size: 13,
color: Colors.green.shade400,
),
],
),
),
),
);
}
}
/// زر نوع الطلب - تصميم تفاعلي حديث
class _OrderTypeButton extends StatelessWidget {
final MapPassengerController controller;
const _OrderTypeButton({required this.controller});
@override
Widget build(BuildContext context) {
final bool isOther = controller.isAnotherOreder;
final Color accent =
isOther ? Colors.indigo.shade500 : AppColor.primaryColor;
return Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
showCupertinoModalPopup(
context: context,
builder: (ctx) => CupertinoActionSheet(
title: Text('Select Order Type'.tr),
message: Text('Choose who this order is for'.tr),
actions: [
CupertinoActionSheetAction(
child: Text('I want to order for myself'.tr),
onPressed: () {
controller.changeisAnotherOreder(false);
Navigator.pop(ctx);
},
),
CupertinoActionSheetAction(
child: Text('I want to order for someone else'.tr),
onPressed: () {
controller.changeisAnotherOreder(true);
Navigator.pop(ctx);
},
),
],
cancelButton: CupertinoActionSheetAction(
isDefaultAction: true,
onPressed: () => Navigator.pop(ctx),
child: Text('Cancel'.tr),
),
),
);
},
borderRadius: BorderRadius.circular(_D.radiusInner),
child: AnimatedContainer(
duration: _D.fast,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
accent.withOpacity(0.08),
accent.withOpacity(0.04),
],
),
borderRadius: BorderRadius.circular(_D.radiusInner),
border: Border.all(
color: accent.withOpacity(0.22),
width: 1,
),
),
child: Row(
children: [
AnimatedContainer(
duration: _D.fast,
width: 34,
height: 34,
decoration: BoxDecoration(
color: accent.withOpacity(0.14),
borderRadius: BorderRadius.circular(11),
border: Border.all(
color: accent.withOpacity(0.25),
width: 1,
),
),
child: Icon(
isOther ? Icons.person_rounded : Icons.group_rounded,
color: accent,
size: 17,
),
),
const SizedBox(width: 14),
Expanded(
child: Text(
isOther ? 'Order for myself'.tr : 'Order for someone else'.tr,
style: TextStyle(
color: accent,
fontSize: 13.5,
fontWeight: FontWeight.w600,
letterSpacing: -0.2,
),
),
),
AnimatedRotation(
duration: _D.fast,
turns: isOther ? 0.5 : 0,
child: Icon(
Icons.unfold_more_rounded,
color: accent.withOpacity(0.55),
size: 19,
),
),
],
),
),
),
);
}
}
// ─────────────────────────────────────────────────────────────────────────────
// FAVOURITE PLACES DIALOG - Modern Modal Design
// ─────────────────────────────────────────────────────────────────────────────
class FaviouratePlacesDialog extends StatelessWidget {
const FaviouratePlacesDialog({super.key});
@override
Widget build(BuildContext context) {
Get.put(MapPassengerController());
return GetBuilder<MapPassengerController>(
builder: (controller) => Center(
child: InkWell(
borderRadius: BorderRadius.circular(14),
onTap: () async {
final List favoritePlaces =
await sql.getAllData(TableName.placesFavorite);
Get.defaultDialog(
title: 'Favorite Places'.tr,
titleStyle: AppStyle.title,
content: SizedBox(
width: Get.width * .85,
height: 300,
child: favoritePlaces.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedContainer(
duration: _D.medium,
width: 76,
height: 76,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColor.accentColor.withOpacity(0.15),
AppColor.accentColor.withOpacity(0.05),
],
),
shape: BoxShape.circle,
border: Border.all(
color: AppColor.accentColor.withOpacity(0.2),
width: 1,
),
),
child: const Icon(
Icons.star_border_rounded,
size: 38,
color: AppColor.accentColor,
),
),
const SizedBox(height: 16),
Text(
'No favorite places yet!'.tr,
style: AppStyle.title.copyWith(
fontWeight: FontWeight.w600,
fontSize: 15,
),
),
],
),
)
: ListView.separated(
itemCount: favoritePlaces.length,
separatorBuilder: (_, __) =>
Divider(height: 1, color: Colors.grey.shade100),
itemBuilder: (context, index) => ListTile(
contentPadding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 4),
leading: AnimatedContainer(
duration: _D.fast,
width: 38,
height: 38,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.amber.shade50,
Colors.amber.shade100.withOpacity(0.5),
],
),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Colors.amber.shade100,
width: 1,
),
),
child: const Icon(
Icons.star,
color: Colors.amber,
size: 19,
),
),
title: Text(
favoritePlaces[index]['name'],
style: AppStyle.title.copyWith(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
trailing: IconButton(
icon: const Icon(
Icons.delete_outline,
color: Colors.redAccent,
),
onPressed: () async {
await sql.deleteData(TableName.placesFavorite,
favoritePlaces[index]['id']);
Get.back();
Toast.show(
context,
'${'Deleted'.tr} ${favoritePlaces[index]['name']}',
AppColor.redColor,
);
},
),
onTap: () async {
Get.back();
await controller.getLocation();
await controller.getDirectionMap(
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
'${favoritePlaces[index]['latitude']},${favoritePlaces[index]['longitude']}',
);
controller.showBottomSheet1();
},
),
),
),
confirm: MyElevatedButton(
title: 'Back'.tr, onPressed: () => Get.back()),
);
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedContainer(
duration: _D.fast,
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: AppColor.accentColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: const Icon(
Icons.star_border_rounded,
color: AppColor.accentColor,
size: 21,
),
),
const SizedBox(width: 10),
Text(
'Favorite Places'.tr,
style: AppStyle.title.copyWith(
fontWeight: FontWeight.w600,
fontSize: 14,
),
),
],
),
),
),
),
);
}
}