From b2cb9b5dc5d0605a982426029c85cabbd959da82 Mon Sep 17 00:00:00 2001 From: Abdusalom G'ayratov Date: Fri, 28 Nov 2025 18:41:40 +0500 Subject: [PATCH] BASE: Finish Cab Type Section UI. --- .../cab_booking_screen.dart | 299 +++++++++--------- lib/widget/osm_map/map_picker_page.dart | 98 ++++-- .../place_picker/location_picker_screen.dart | 92 +++++- 3 files changed, 298 insertions(+), 191 deletions(-) diff --git a/lib/screen_ui/cab_service_screens/cab_booking_screen.dart b/lib/screen_ui/cab_service_screens/cab_booking_screen.dart index 7df5709..04f7cd1 100644 --- a/lib/screen_ui/cab_service_screens/cab_booking_screen.dart +++ b/lib/screen_ui/cab_service_screens/cab_booking_screen.dart @@ -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,45 +484,45 @@ 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: [ - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - color: AppThemeData.grey400, - ), - height: 4, - width: 33, - ), - 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, + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: AppThemeData.grey400, ), - 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( 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,141 +532,128 @@ class CabBookingScreen extends StatelessWidget { controller.selectedVehicleType.value = controller.vehicleTypes[index]; }, - child: Padding( - padding: const EdgeInsets.only(bottom: 10), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(15), - border: Border.all( - color: - isDark - ? controller + 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(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 .value .id == vehicleType.id - ? Colors.white - : AppThemeData.grey500 - : controller - .selectedVehicleType - .value - .id == - vehicleType.id - ? AppThemeData.grey300 - : Colors.transparent, - width: 1, - ), - color: - controller.selectedVehicleType.value.id == - vehicleType.id - ? AppThemeData.grey50 - : isDark - ? AppThemeData.grey300 - : Colors.white, - ), - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 10, - horizontal: 10, - ), - child: Row( - children: [ - ClipRRect( - //borderRadius: BorderRadius.circular(10), - child: CachedNetworkImage( - imageUrl: - vehicleType.vehicleIcon - .toString(), - height: 60, - width: 60, - imageBuilder: - (context, imageProvider) => - Container( - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular( - 10, - ), - image: DecorationImage( - image: imageProvider, - fit: BoxFit.cover, - ), - ), - ), - placeholder: - (context, url) => Center( - child: - CircularProgressIndicator.adaptive( - valueColor: - AlwaysStoppedAnimation( - AppThemeData - .primary300, - ), - ), - ), - errorWidget: - (context, url, error) => - ClipRRect( + ? 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(), + + imageBuilder: + (context, imageProvider) => + Container( + decoration: BoxDecoration( borderRadius: BorderRadius.circular( - 20, + 10, ), - child: Image.network( - Constant.placeHolderImage, + image: DecorationImage( + image: imageProvider, 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, ), - ), - Padding( - padding: const EdgeInsets.only( - top: 2.0, - ), - child: Text( - controller.duration.value, - style: const TextStyle( - fontWeight: FontWeight.w400, - letterSpacing: 1, + placeholder: + (context, url) => Center( + child: + CircularProgressIndicator.adaptive( + valueColor: + AlwaysStoppedAnimation( + AppThemeData + .primary300, + ), ), - ), + ), + 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( () => 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, ), ), ], diff --git a/lib/widget/osm_map/map_picker_page.dart b/lib/widget/osm_map/map_picker_page.dart index 4e55913..334c7c9 100644 --- a/lib/widget/osm_map/map_picker_page.dart +++ b/lib/widget/osm_map/map_picker_page.dart @@ -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(); 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, + ), ], ), ], diff --git a/lib/widget/place_picker/location_picker_screen.dart b/lib/widget/place_picker/location_picker_screen.dart index efb1fdc..38d6dc8 100644 --- a/lib/widget/place_picker/location_picker_screen.dart +++ b/lib/widget/place_picker/location_picker_screen.dart @@ -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(), + ), ], ), ),