BASE: Finish Cab Type Section UI.

This commit is contained in:
2025-11-28 18:41:40 +05:00
parent 50aae9ce1a
commit b2cb9b5dc5
3 changed files with 298 additions and 191 deletions

View File

@@ -14,6 +14,7 @@ import 'package:customer/utils/network_image_widget.dart';
import 'package:customer/utils/utils.dart';
import 'package:dotted_border/dotted_border.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
@@ -469,9 +470,9 @@ class CabBookingScreen extends StatelessWidget {
) {
return Positioned.fill(
child: DraggableScrollableSheet(
initialChildSize: 0.40,
minChildSize: 0.40,
maxChildSize: 0.8,
initialChildSize: 0.37,
minChildSize: 0.37,
maxChildSize: 0.37,
expand: false,
builder: (context, scrollController) {
return Container(
@@ -483,29 +484,30 @@ class CabBookingScreen extends StatelessWidget {
),
),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 15.0,
vertical: 10,
),
padding: EdgeInsets.symmetric(vertical: 10.r),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: AppThemeData.grey400,
),
height: 4,
width: 33,
height: 4.h,
width: 33.w,
),
Align(
alignment: Alignment.topLeft,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
],
),
SizedBox(height: 15.h),
Padding(
padding: EdgeInsetsGeometry.symmetric(horizontal: 16.r),
child: Text(
"Select Your Vehicle Type".tr,
style: AppThemeData.boldTextStyle(
fontSize: 18,
fontSize: 18.sp,
color:
isDark
? AppThemeData.greyDark900
@@ -514,14 +516,13 @@ class CabBookingScreen extends StatelessWidget {
textAlign: TextAlign.start,
),
),
),
SizedBox(height: 10.h),
Expanded(
child: ListView.builder(
itemCount: controller.vehicleTypes.length,
shrinkWrap: true,
padding: EdgeInsets.only(bottom: 20),
physics: AlwaysScrollableScrollPhysics(),
controller: scrollController,
scrollDirection: Axis.vertical,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
VehicleType vehicleType =
controller.vehicleTypes[index];
@@ -531,53 +532,90 @@ class CabBookingScreen extends StatelessWidget {
controller.selectedVehicleType.value =
controller.vehicleTypes[index];
},
child: Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Container(
width: 130.w,
margin:
index == 0
? EdgeInsets.only(left: 16.r, right: 10.r)
: index ==
controller.vehicleTypes.length - 1
? EdgeInsets.only(right: 10.r)
: EdgeInsets.only(right: 10.r),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
border: Border.all(
color:
isDark
? controller
.selectedVehicleType
.value
.id ==
vehicleType.id
? Colors.white
: AppThemeData.grey500
: controller
.selectedVehicleType
.value
.id ==
vehicleType.id
? AppThemeData.grey300
: Colors.transparent,
width: 1,
),
borderRadius: BorderRadius.circular(16.r),
color:
controller.selectedVehicleType.value.id ==
vehicleType.id
? AppThemeData.grey50
: isDark
? AppThemeData.grey300
: Colors.white,
? AppThemeData.mainColor
: AppThemeData.grey300,
),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 10,
horizontal: 10,
),
child: Row(
padding: EdgeInsets.fromLTRB(8.r, 8.r, 0, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
//borderRadius: BorderRadius.circular(10),
Text(
// "${vehicleType.name} | ${controller.distance.toStringAsFixed(2)}${'km'.tr}",
"${vehicleType.name}",
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontFamily: "Inter",
fontSize: 15.sp,
overflow: TextOverflow.ellipsis,
color:
controller
.selectedVehicleType
.value
.id ==
vehicleType.id
? AppThemeData.grey50
: AppThemeData.darkGrey,
fontWeight: FontWeight.bold,
),
),
Text(
Constant.amountShow(
amount:
controller
.getAmount(vehicleType)
.toString(),
),
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w500,
color:
controller
.selectedVehicleType
.value
.id ==
vehicleType.id
? AppThemeData.grey50
: AppThemeData.darkGrey
.withValues(alpha: 0.7),
),
),
Text(
"(${controller.duration.value})",
style: TextStyle(
fontWeight: FontWeight.w400,
color:
controller
.selectedVehicleType
.value
.id ==
vehicleType.id
? AppThemeData.grey50
: AppThemeData.darkGrey
.withValues(alpha: 0.7),
),
),
Expanded(
child: CachedNetworkImage(
imageUrl:
vehicleType.vehicleIcon
.toString(),
height: 60,
width: 60,
vehicleType.vehicleIcon.toString(),
imageBuilder:
(context, imageProvider) =>
Container(
@@ -604,12 +642,9 @@ class CabBookingScreen extends StatelessWidget {
),
),
errorWidget:
(context, url, error) =>
ClipRRect(
(context, url, error) => ClipRRect(
borderRadius:
BorderRadius.circular(
20,
),
BorderRadius.circular(20),
child: Image.network(
Constant.placeHolderImage,
fit: BoxFit.cover,
@@ -618,62 +653,16 @@ class CabBookingScreen extends StatelessWidget {
fit: BoxFit.cover,
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
"${vehicleType.name} | ${controller.distance.toStringAsFixed(2)}${'km'.tr}",
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
letterSpacing: 1,
),
),
Padding(
padding: const EdgeInsets.only(
top: 2.0,
),
child: Text(
controller.duration.value,
style: const TextStyle(
fontWeight: FontWeight.w400,
letterSpacing: 1,
),
),
),
],
),
),
),
Text(
Constant.amountShow(
amount:
controller
.getAmount(vehicleType)
.toString(),
),
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
letterSpacing: 1,
),
),
],
),
),
),
),
),
);
},
),
),
SizedBox(height: 15.h),
Obx(
() => RoundedButtonFill(
title: 'pay_amount'.trParams({
@@ -701,8 +690,8 @@ class CabBookingScreen extends StatelessWidget {
);
}
},
color: AppThemeData.primary300,
textColor: AppThemeData.grey900,
color: AppThemeData.mainColor,
textColor: AppThemeData.grey50,
),
),
],

View File

@@ -1,8 +1,11 @@
import 'dart:developer';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/round_button_fill.dart';
import 'package:customer/widget/osm_map/map_controller.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:latlong2/latlong.dart';
import '../../controllers/theme_controller.dart';
@@ -18,38 +21,68 @@ class MapPickerPage extends StatelessWidget {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return Scaffold(
appBar: AppBar(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
centerTitle: false,
titleSpacing: 0,
title: Text("PickUp Location".tr, textAlign: TextAlign.start, style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900)),
),
// appBar: AppBar(
// backgroundColor:
// isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
// centerTitle: false,
// titleSpacing: 0,
// title: Text(
// "PickUp Location".tr,
// textAlign: TextAlign.start,
// style: TextStyle(
// fontFamily: AppThemeData.medium,
// fontSize: 16,
// color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
// ),
// ),
// ),
body: Stack(
children: [
Obx(
() => FlutterMap(
mapController: controller.mapController,
options: MapOptions(
initialCenter: controller.pickedPlace.value?.coordinates ?? LatLng(20.5937, 78.9629), // Default India center
initialCenter:
controller.pickedPlace.value?.coordinates ??
LatLng(20.5937, 78.9629), // Default India center
initialZoom: 13,
onTap: (tapPos, latlng) {
controller.addLatLngOnly(latlng);
controller.mapController.move(latlng, controller.mapController.camera.zoom);
controller.mapController.move(
latlng,
controller.mapController.camera.zoom,
);
},
),
children: [
TileLayer(urlTemplate: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', subdomains: const ['a', 'b', 'c'], userAgentPackageName: 'com.emart.app'),
TileLayer(
urlTemplate:
'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
subdomains: const ['a', 'b', 'c'],
userAgentPackageName: 'com.emart.app',
),
MarkerLayer(
markers:
controller.pickedPlace.value != null
? [Marker(point: controller.pickedPlace.value!.coordinates, width: 40, height: 40, child: const Icon(Icons.location_pin, size: 36, color: Colors.red))]
? [
Marker(
point: controller.pickedPlace.value!.coordinates,
width: 40,
height: 40,
child: const Icon(
Icons.location_pin,
size: 36,
color: Colors.red,
),
),
]
: [],
),
],
),
),
Positioned(
top: 16,
top: MediaQuery.of(context).size.height / 9,
left: 16,
right: 16,
child: Column(
@@ -59,10 +92,18 @@ class MapPickerPage extends StatelessWidget {
borderRadius: BorderRadius.circular(8),
child: TextField(
controller: searchController,
style: TextStyle(color: isDark ? AppThemeData.grey900 : AppThemeData.grey900),
style: TextStyle(
color:
isDark ? AppThemeData.grey900 : AppThemeData.grey900,
),
decoration: InputDecoration(
hintText: 'Search location...'.tr,
hintStyle: TextStyle(color: isDark ? AppThemeData.grey900 : AppThemeData.grey900),
hintStyle: TextStyle(
color:
isDark
? AppThemeData.grey900
: AppThemeData.grey900,
),
contentPadding: EdgeInsets.all(12),
border: InputBorder.none,
prefixIcon: Icon(Icons.search),
@@ -76,7 +117,10 @@ class MapPickerPage extends StatelessWidget {
}
return Container(
margin: const EdgeInsets.only(top: 4),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(8)),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: ListView.builder(
shrinkWrap: true,
itemCount: controller.searchResults.length,
@@ -111,8 +155,13 @@ class MapPickerPage extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
Text(
controller.pickedPlace.value != null ? "Picked Location:".tr : "No Location Picked".tr,
style: TextStyle(color: isDark ? AppThemeData.primary300 : AppThemeData.primary300, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600),
controller.pickedPlace.value != null
? "Picked Location:".tr
: "No Location Picked".tr,
style: AppThemeData.boldTextStyle(
color: AppThemeData.grey900,
fontSize: 17.sp,
),
),
const SizedBox(height: 4),
if (controller.pickedPlace.value != null)
@@ -129,20 +178,25 @@ class MapPickerPage extends StatelessWidget {
Expanded(
child: RoundedButtonFill(
title: "Confirm Location".tr,
color: AppThemeData.primary300,
color: AppThemeData.mainColor,
textColor: AppThemeData.grey50,
height: 5,
height: 4.h,
onPress: () async {
final selected = controller.pickedPlace.value;
if (selected != null) {
Get.back(result: selected); // ✅ Return the selected place
print("Selected location: $selected");
Get.back(
result: selected,
); // ✅ Return the selected place
log("Selected location: $selected");
}
},
),
),
const SizedBox(width: 10),
IconButton(icon: const Icon(Icons.delete_forever, color: Colors.red), onPressed: controller.clearAll),
SizedBox(width: 10.w),
IconButton(
icon: const Icon(Icons.delete_forever, color: Colors.red),
onPressed: controller.clearAll,
),
],
),
],

View File

@@ -31,7 +31,10 @@ class LocationPickerScreen extends StatelessWidget {
onMapCreated: (controllers) {
controller.mapController = controllers;
},
initialCameraPosition: CameraPosition(target: controller.selectedLocation.value!, zoom: 15),
initialCameraPosition: CameraPosition(
target: controller.selectedLocation.value!,
zoom: 15,
),
onTap: (LatLng tappedPosition) {
controller.selectedLocation.value = tappedPosition;
controller.getAddressFromLatLng(tappedPosition);
@@ -44,14 +47,18 @@ class LocationPickerScreen extends StatelessWidget {
markerId: const MarkerId("selected-location"),
position: controller.selectedLocation.value!,
onTap: () {
controller.getAddressFromLatLng(controller.selectedLocation.value!);
controller.getAddressFromLatLng(
controller.selectedLocation.value!,
);
},
),
},
onCameraMove: controller.onMapMoved,
onCameraIdle: () {
if (controller.selectedLocation.value != null) {
controller.getAddressFromLatLng(controller.selectedLocation.value!);
controller.getAddressFromLatLng(
controller.selectedLocation.value!,
);
}
},
),
@@ -67,29 +74,65 @@ class LocationPickerScreen extends StatelessWidget {
Get.back();
},
child: Container(
decoration: BoxDecoration(color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50, borderRadius: BorderRadius.circular(30)),
child: Padding(padding: const EdgeInsets.all(10), child: Icon(Icons.arrow_back_ios_new_outlined, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
decoration: BoxDecoration(
color:
isDark
? AppThemeData.greyDark50
: AppThemeData.grey50,
borderRadius: BorderRadius.circular(30),
),
child: Padding(
padding: const EdgeInsets.all(10),
child: Icon(
Icons.arrow_back_ios_new_outlined,
color:
isDark
? AppThemeData.greyDark900
: AppThemeData.grey900,
),
),
),
),
SizedBox(height: 20),
GestureDetector(
onTap: () async {
Prediction? p = await PlacesAutocomplete.show(context: context, apiKey: Constant.mapAPIKey, mode: Mode.overlay, language: "en");
Prediction? p = await PlacesAutocomplete.show(
context: context,
apiKey: Constant.mapAPIKey,
mode: Mode.overlay,
language: "en",
);
if (p != null) {
final detail = await _places.getDetailsByPlaceId(p.placeId!);
final detail = await _places.getDetailsByPlaceId(
p.placeId!,
);
final lat = detail.result.geometry!.location.lat;
final lng = detail.result.geometry!.location.lng;
final LatLng pos = LatLng(lat, lng);
controller.selectedLocation.value = pos;
controller.mapController?.animateCamera(CameraUpdate.newLatLngZoom(pos, 15));
controller.mapController?.animateCamera(
CameraUpdate.newLatLngZoom(pos, 15),
);
controller.getAddressFromLatLng(pos);
}
},
child: Container(
width: Responsive.width(100, context),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(60)),
child: Row(children: [Icon(Icons.search), SizedBox(width: 8), Text("Search place...".tr)]),
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(60),
),
child: Row(
children: [
Icon(Icons.search),
SizedBox(width: 8),
Text("Search place...".tr),
],
),
),
),
],
@@ -101,13 +144,34 @@ class LocationPickerScreen extends StatelessWidget {
right: 20,
child: Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(10), boxShadow: const [BoxShadow(color: Colors.black26, blurRadius: 5)]),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: const [
BoxShadow(color: Colors.black26, blurRadius: 5),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Obx(() => Text(controller.address.value, textAlign: TextAlign.center, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500))),
Obx(
() => Text(
controller.address.value,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
const SizedBox(height: 10),
RoundedButtonFill(title: "Confirm Location".tr, height: 5.5, color: AppThemeData.primary300, textColor: AppThemeData.grey50, onPress: () => controller.confirmLocation()),
RoundedButtonFill(
title: "Confirm Location",
height: 5.5,
color: AppThemeData.mainColor,
textColor: AppThemeData.grey50,
onPress: () => controller.confirmLocation(),
),
],
),
),