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

View File

@@ -1,8 +1,11 @@
import 'dart:developer';
import 'package:customer/themes/app_them_data.dart'; import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/round_button_fill.dart'; import 'package:customer/themes/round_button_fill.dart';
import 'package:customer/widget/osm_map/map_controller.dart'; import 'package:customer/widget/osm_map/map_controller.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
import '../../controllers/theme_controller.dart'; import '../../controllers/theme_controller.dart';
@@ -18,38 +21,68 @@ class MapPickerPage extends StatelessWidget {
final themeController = Get.find<ThemeController>(); final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value; final isDark = themeController.isDark.value;
return Scaffold( return Scaffold(
appBar: AppBar( // appBar: AppBar(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface, // backgroundColor:
centerTitle: false, // isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
titleSpacing: 0, // centerTitle: false,
title: Text("PickUp Location".tr, textAlign: TextAlign.start, style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900)), // 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( body: Stack(
children: [ children: [
Obx( Obx(
() => FlutterMap( () => FlutterMap(
mapController: controller.mapController, mapController: controller.mapController,
options: MapOptions( 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, initialZoom: 13,
onTap: (tapPos, latlng) { onTap: (tapPos, latlng) {
controller.addLatLngOnly(latlng); controller.addLatLngOnly(latlng);
controller.mapController.move(latlng, controller.mapController.camera.zoom); controller.mapController.move(
latlng,
controller.mapController.camera.zoom,
);
}, },
), ),
children: [ 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( MarkerLayer(
markers: markers:
controller.pickedPlace.value != null 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( Positioned(
top: 16, top: MediaQuery.of(context).size.height / 9,
left: 16, left: 16,
right: 16, right: 16,
child: Column( child: Column(
@@ -59,10 +92,18 @@ class MapPickerPage extends StatelessWidget {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
child: TextField( child: TextField(
controller: searchController, controller: searchController,
style: TextStyle(color: isDark ? AppThemeData.grey900 : AppThemeData.grey900), style: TextStyle(
color:
isDark ? AppThemeData.grey900 : AppThemeData.grey900,
),
decoration: InputDecoration( decoration: InputDecoration(
hintText: 'Search location...'.tr, hintText: 'Search location...'.tr,
hintStyle: TextStyle(color: isDark ? AppThemeData.grey900 : AppThemeData.grey900), hintStyle: TextStyle(
color:
isDark
? AppThemeData.grey900
: AppThemeData.grey900,
),
contentPadding: EdgeInsets.all(12), contentPadding: EdgeInsets.all(12),
border: InputBorder.none, border: InputBorder.none,
prefixIcon: Icon(Icons.search), prefixIcon: Icon(Icons.search),
@@ -76,7 +117,10 @@ class MapPickerPage extends StatelessWidget {
} }
return Container( return Container(
margin: const EdgeInsets.only(top: 4), 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( child: ListView.builder(
shrinkWrap: true, shrinkWrap: true,
itemCount: controller.searchResults.length, itemCount: controller.searchResults.length,
@@ -111,8 +155,13 @@ class MapPickerPage extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( Text(
controller.pickedPlace.value != null ? "Picked Location:".tr : "No Location Picked".tr, controller.pickedPlace.value != null
style: TextStyle(color: isDark ? AppThemeData.primary300 : AppThemeData.primary300, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600), ? "Picked Location:".tr
: "No Location Picked".tr,
style: AppThemeData.boldTextStyle(
color: AppThemeData.grey900,
fontSize: 17.sp,
),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
if (controller.pickedPlace.value != null) if (controller.pickedPlace.value != null)
@@ -129,20 +178,25 @@ class MapPickerPage extends StatelessWidget {
Expanded( Expanded(
child: RoundedButtonFill( child: RoundedButtonFill(
title: "Confirm Location".tr, title: "Confirm Location".tr,
color: AppThemeData.primary300, color: AppThemeData.mainColor,
textColor: AppThemeData.grey50, textColor: AppThemeData.grey50,
height: 5, height: 4.h,
onPress: () async { onPress: () async {
final selected = controller.pickedPlace.value; final selected = controller.pickedPlace.value;
if (selected != null) { if (selected != null) {
Get.back(result: selected); // ✅ Return the selected place Get.back(
print("Selected location: $selected"); result: selected,
); // ✅ Return the selected place
log("Selected location: $selected");
} }
}, },
), ),
), ),
const SizedBox(width: 10), SizedBox(width: 10.w),
IconButton(icon: const Icon(Icons.delete_forever, color: Colors.red), onPressed: controller.clearAll), 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) { onMapCreated: (controllers) {
controller.mapController = controllers; controller.mapController = controllers;
}, },
initialCameraPosition: CameraPosition(target: controller.selectedLocation.value!, zoom: 15), initialCameraPosition: CameraPosition(
target: controller.selectedLocation.value!,
zoom: 15,
),
onTap: (LatLng tappedPosition) { onTap: (LatLng tappedPosition) {
controller.selectedLocation.value = tappedPosition; controller.selectedLocation.value = tappedPosition;
controller.getAddressFromLatLng(tappedPosition); controller.getAddressFromLatLng(tappedPosition);
@@ -44,14 +47,18 @@ class LocationPickerScreen extends StatelessWidget {
markerId: const MarkerId("selected-location"), markerId: const MarkerId("selected-location"),
position: controller.selectedLocation.value!, position: controller.selectedLocation.value!,
onTap: () { onTap: () {
controller.getAddressFromLatLng(controller.selectedLocation.value!); controller.getAddressFromLatLng(
controller.selectedLocation.value!,
);
}, },
), ),
}, },
onCameraMove: controller.onMapMoved, onCameraMove: controller.onMapMoved,
onCameraIdle: () { onCameraIdle: () {
if (controller.selectedLocation.value != null) { 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(); Get.back();
}, },
child: Container( child: Container(
decoration: BoxDecoration(color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50, borderRadius: BorderRadius.circular(30)), decoration: BoxDecoration(
child: Padding(padding: const EdgeInsets.all(10), child: Icon(Icons.arrow_back_ios_new_outlined, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)), 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), SizedBox(height: 20),
GestureDetector( GestureDetector(
onTap: () async { 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) { 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 lat = detail.result.geometry!.location.lat;
final lng = detail.result.geometry!.location.lng; final lng = detail.result.geometry!.location.lng;
final LatLng pos = LatLng(lat, lng); final LatLng pos = LatLng(lat, lng);
controller.selectedLocation.value = pos; controller.selectedLocation.value = pos;
controller.mapController?.animateCamera(CameraUpdate.newLatLngZoom(pos, 15)); controller.mapController?.animateCamera(
CameraUpdate.newLatLngZoom(pos, 15),
);
controller.getAddressFromLatLng(pos); controller.getAddressFromLatLng(pos);
} }
}, },
child: Container( child: Container(
width: Responsive.width(100, context), width: Responsive.width(100, context),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), padding: const EdgeInsets.symmetric(
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(60)), horizontal: 16,
child: Row(children: [Icon(Icons.search), SizedBox(width: 8), Text("Search place...".tr)]), 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, right: 20,
child: Container( child: Container(
padding: const EdgeInsets.all(10), 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( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ 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), 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(),
),
], ],
), ),
), ),