second commit
This commit is contained in:
951
lib/View/Orders/Deliverydetails/Deliverydetailsview.dart
Normal file
951
lib/View/Orders/Deliverydetails/Deliverydetailsview.dart
Normal file
@@ -0,0 +1,951 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get/get_core/src/get_main.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import '../../../Controller/map_controller.dart';
|
||||
import '../../../Globalwidgets/textwidget.dart';
|
||||
import '../../../Helper/Constants/Colorconstants.dart';
|
||||
import '../../../Helper/utility.dart';
|
||||
import '../../../Model/Response/Summary/Getsummarysresponse.dart';
|
||||
|
||||
|
||||
|
||||
class MapWithBottomSheetPage extends StatelessWidget {
|
||||
|
||||
final DeliveriesDetails data;
|
||||
|
||||
MapWithBottomSheetPage({super.key, required this.data});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
MapWithPolylines(
|
||||
endLatLng: LatLng(
|
||||
double.tryParse(data.deliverylat ?? '') ?? 0.0,
|
||||
double.tryParse(data.deliverylong ?? '') ?? 0.0,
|
||||
),
|
||||
startLatLng: LatLng(
|
||||
double.tryParse(data.pickuplat ?? '') ?? 0.0,
|
||||
double.tryParse(data.pickuplon ?? '') ?? 0.0,
|
||||
),
|
||||
),
|
||||
DraggableScrollableSheet(
|
||||
initialChildSize: 0.22,
|
||||
minChildSize: 0.12,
|
||||
maxChildSize: 0.65,
|
||||
builder: (context, scrollController) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(18)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
blurRadius: 10,
|
||||
spreadRadius: 2,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
controller: scrollController,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 5,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
backgroundColor: ColorConstants.primaryColor,
|
||||
radius: 30,
|
||||
child: TextWidget(
|
||||
text: data.ridername?[0] ?? '',
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 20,
|
||||
color: ColorConstants.secondaryColor,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
data.ridername ?? '',
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
|
||||
),
|
||||
Text("Order ID: ${data.orderid}"),
|
||||
Text("Status: ${data.orderstatus}", style: TextStyle(color: Colors.green)),
|
||||
],
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.phone, color: ColorConstants.primaryColor),
|
||||
onPressed: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
Divider(),
|
||||
ListTile(
|
||||
title: Text("Pickup Location"),
|
||||
subtitle: Text(data.pickupaddress ?? ''),
|
||||
leading: Icon(Icons.location_on, color: Colors.red),
|
||||
),
|
||||
SizedBox(height: 10,),
|
||||
ListTile(
|
||||
title: Text("Drop-off Location"),
|
||||
subtitle: Text(data.deliveryaddress ?? ''),
|
||||
leading: Icon(Icons.flag, color: Colors.green),
|
||||
),
|
||||
SizedBox(height: 18),
|
||||
Divider(
|
||||
height: 1,
|
||||
color: Colors.grey,
|
||||
),
|
||||
SizedBox(height: 18),
|
||||
TextWidget(
|
||||
text: 'Order Details',
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
),
|
||||
SizedBox(height: 10,),
|
||||
Row(
|
||||
children: [
|
||||
TextWidget(
|
||||
text: 'Delivery Charges',
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
Spacer(),
|
||||
TextWidget(
|
||||
text: data.deliverycharges.toString(),
|
||||
fontWeight: FontWeight.w700,
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10,),
|
||||
Row(
|
||||
children: [
|
||||
TextWidget(
|
||||
text: 'Total Amount',
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
Spacer(),
|
||||
TextWidget(
|
||||
text: data.deliveryamt.toString(),
|
||||
fontWeight: FontWeight.w700,
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 18),
|
||||
Divider(
|
||||
height: 1,
|
||||
color: Colors.grey,
|
||||
),
|
||||
SizedBox(height: 18),
|
||||
TextWidget(
|
||||
text: 'Payment Details',
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
),
|
||||
SizedBox(height: 10,),
|
||||
Row(
|
||||
children: [
|
||||
TextWidget(
|
||||
text: data.deliverycustomer ?? '',
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
Spacer(),
|
||||
TextWidget(
|
||||
text: data.deliverycontactno.toString(),
|
||||
fontWeight: FontWeight.w700,
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 18),
|
||||
Divider(
|
||||
height: 1,
|
||||
color: Colors.grey,
|
||||
),
|
||||
SizedBox(height: 18),
|
||||
TextWidget(
|
||||
text: 'Payment method',
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
),
|
||||
SizedBox(height: 10,),
|
||||
TextWidget(
|
||||
text: 'Cash on delivery',
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// class SummaryDetailsView extends StatelessWidget {
|
||||
// final DeliveriesDetails data;
|
||||
//
|
||||
// const SummaryDetailsView({super.key, required this.data});
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Scaffold(
|
||||
// appBar: _buildAppBar(),
|
||||
// backgroundColor: Colors.grey[100],
|
||||
// body: _buildBody(context),
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// /// Builds the app bar with a back button and title.
|
||||
// AppBar _buildAppBar() {
|
||||
// return AppBar(
|
||||
// leading: InkWell(
|
||||
// onTap: Get.back,
|
||||
// child: Icon(
|
||||
// Icons.arrow_back,
|
||||
// color: ColorConstants.blackColor,
|
||||
// size: 28,
|
||||
// ),
|
||||
// ),
|
||||
// title: TextWidget(
|
||||
// text: 'Delivery Details',
|
||||
// fontSize: 20,
|
||||
// fontWeight: FontWeight.w700,
|
||||
// ),
|
||||
// backgroundColor: ColorConstants.secondaryColor,
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// /// Builds the scrollable body with delivery details and map.
|
||||
// Widget _buildBody(BuildContext context) {
|
||||
// return SingleChildScrollView(
|
||||
// padding: const EdgeInsets.all(0.0),
|
||||
// child: Column(
|
||||
// children: [
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.only(left: 0,right: 0),
|
||||
// child: SizedBox(
|
||||
// height: Get.height * 0.60,
|
||||
// child: MapWithPolylines(
|
||||
// endLatLng: LatLng(
|
||||
// double.tryParse(data.deliverylat ?? '') ?? 0.0,
|
||||
// double.tryParse(data.deliverylong ?? '') ?? 0.0,
|
||||
// ),
|
||||
// startLatLng: LatLng(
|
||||
// double.tryParse(data.pickuplat ?? '') ?? 0.0,
|
||||
// double.tryParse(data.pickuplon ?? '') ?? 0.0,
|
||||
// ),
|
||||
// )
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 10),
|
||||
// DeliveryCard(data: data, isFromSummary: true,),
|
||||
// const SizedBox(height: 20,)
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
/// A reusable card widget displaying delivery header information (date, tenant, order).
|
||||
class DeliveryHeaderCard extends StatelessWidget {
|
||||
final DeliveriesDetails data;
|
||||
|
||||
const DeliveryHeaderCard({super.key, required this.data});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: ColorConstants.secondaryColor,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 5),
|
||||
height: 95,
|
||||
child: Row(
|
||||
children: [
|
||||
_buildDateSection(),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
IconTextRow(
|
||||
icon: Icons.person,
|
||||
text: data.tenantname ?? '',
|
||||
textStyle: _textStyle(15, Colors.grey[500]),
|
||||
),
|
||||
IconTextRow(
|
||||
icon: Icons.receipt,
|
||||
text: data.orderid ?? '',
|
||||
textStyle: _textStyle(13, Colors.black54),
|
||||
),
|
||||
IconTextRow(
|
||||
icon: Icons.phone,
|
||||
text: data.tenantcontactno ?? '',
|
||||
textStyle: _textStyle(13, Colors.black54),
|
||||
onTap: () => _launchPhone(data.tenantcontactno),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Builds the date section with day, month, and time.
|
||||
Widget _buildDateSection() {
|
||||
final date = DateFormat("yyyy-MM-dd", "en_US").parse(data.deliverydate ?? DateTime.now().toString());
|
||||
return Container(
|
||||
width: 75,
|
||||
decoration: BoxDecoration(
|
||||
color: ColorConstants.primaryColor1,
|
||||
shape: BoxShape.rectangle,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white70,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
DateFormat("dd").format(date),
|
||||
style: _textStyle(14, Colors.grey[700]),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
DateFormat("MMM").format(date),
|
||||
style: _textStyle(14, Colors.grey[700], height: 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
DateFormat("hh.mm a").format(date),
|
||||
style: _textStyle(11, Colors.grey[700]),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Helper method to create consistent text styles.
|
||||
TextStyle _textStyle(double fontSize, Color? color, {double? height}) {
|
||||
return TextStyle(
|
||||
fontSize: fontSize,
|
||||
color: color,
|
||||
fontWeight: FontWeight.w600,
|
||||
height: height,
|
||||
);
|
||||
}
|
||||
|
||||
/// Launches phone dialer with the provided number.
|
||||
void _launchPhone(String? number) {
|
||||
if (number != null && number.isNotEmpty) {
|
||||
launch('tel://$number');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A reusable card widget for displaying pickup or delivery location details.
|
||||
class DeliveryLocationCard extends StatelessWidget {
|
||||
final String title;
|
||||
final String address;
|
||||
final String contact;
|
||||
final double lat;
|
||||
final double lon;
|
||||
final Color iconColor;
|
||||
final bool isPickup;
|
||||
|
||||
const DeliveryLocationCard({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.address,
|
||||
required this.contact,
|
||||
required this.lat,
|
||||
required this.lon,
|
||||
required this.iconColor,
|
||||
required this.isPickup,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
color: Colors.red[100]!.withAlpha(100),
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(13)),
|
||||
child: SizedBox(
|
||||
height: 150,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
const SizedBox(height: 15),
|
||||
_buildMarkerIcon(),
|
||||
const Spacer(),
|
||||
_buildNavigationIcon(),
|
||||
const SizedBox(height: 10),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 14,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: Card(
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 10),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 15),
|
||||
IconTextRow(
|
||||
icon: isPickup ? Icons.business : Icons.person,
|
||||
text: title,
|
||||
textStyle: const TextStyle(color: Colors.black87, fontSize: 15),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
IconTextRow(
|
||||
icon: Icons.location_on_rounded,
|
||||
text: address,
|
||||
textStyle: const TextStyle(
|
||||
color: Colors.black87,
|
||||
fontSize: 13,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
maxLines: 2,
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
IconTextRow(
|
||||
icon: Icons.phone,
|
||||
text: contact,
|
||||
textStyle: const TextStyle(color: Colors.black87, fontSize: 13),
|
||||
onTap: () => Utility.openPhoneCallApp(contact),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Builds the marker icon for the location.
|
||||
Widget _buildMarkerIcon() {
|
||||
return Container(
|
||||
height: 40,
|
||||
width: 40,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.white,
|
||||
),
|
||||
child: FaIcon(
|
||||
FontAwesomeIcons.mapMarkerAlt,
|
||||
size: 22,
|
||||
color: iconColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Builds the navigation icon with a tap action to open the map.
|
||||
Widget _buildNavigationIcon() {
|
||||
return InkWell(
|
||||
onTap: () => Utility.openMap(lat, lon),
|
||||
child: Container(
|
||||
height: 40,
|
||||
width: 40,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.white,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.assistant_direction_rounded,
|
||||
size: 30,
|
||||
color: ColorConstants.primaryColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A reusable widget for displaying an icon and text with an optional tap action.
|
||||
class IconTextRow extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String text;
|
||||
final TextStyle textStyle;
|
||||
final int? maxLines;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
const IconTextRow({
|
||||
super.key,
|
||||
required this.icon,
|
||||
required this.text,
|
||||
required this.textStyle,
|
||||
this.maxLines,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(icon, size: 14, color: Colors.black38),
|
||||
const SizedBox(width: 4),
|
||||
Flexible(
|
||||
child: Text(
|
||||
text,
|
||||
style: textStyle,
|
||||
maxLines: maxLines,
|
||||
overflow: maxLines != null ? TextOverflow.ellipsis : null,
|
||||
),
|
||||
),
|
||||
if (maxLines != null) const SizedBox(width: 5),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Map with polyLines
|
||||
class MapWithPolylines extends StatelessWidget {
|
||||
final LatLng startLatLng;
|
||||
final LatLng endLatLng;
|
||||
|
||||
MapWithPolylines({
|
||||
required this.startLatLng,
|
||||
required this.endLatLng,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
final mapController = Get.put(MapController());
|
||||
|
||||
Set<Polyline> _createPolylines() {
|
||||
return {
|
||||
Polyline(
|
||||
polylineId: const PolylineId('route1'),
|
||||
visible: true,
|
||||
points: [startLatLng, endLatLng],
|
||||
color: Colors.blue,
|
||||
width: 5,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
Obx(() => GoogleMap(
|
||||
initialCameraPosition: CameraPosition(
|
||||
target: startLatLng,
|
||||
zoom: mapController.zoom.value,
|
||||
),
|
||||
polylines: _createPolylines(),
|
||||
markers: {
|
||||
Marker(markerId: const MarkerId("start"), position: startLatLng),
|
||||
Marker(markerId: const MarkerId("end"), position: endLatLng),
|
||||
},
|
||||
onMapCreated: (controller) {
|
||||
mapController.setController(controller);
|
||||
},
|
||||
)),
|
||||
Positioned(
|
||||
top: 20,
|
||||
right: 10,
|
||||
child: Column(
|
||||
children: [
|
||||
FloatingActionButton(
|
||||
mini: true,
|
||||
backgroundColor: Colors.white,
|
||||
onPressed: mapController.zoomIn,
|
||||
child: const Icon(Icons.add, color: Colors.black),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
FloatingActionButton(
|
||||
mini: true,
|
||||
backgroundColor: Colors.white,
|
||||
onPressed: mapController.zoomOut,
|
||||
child: const Icon(Icons.remove, color: Colors.black),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 20,
|
||||
left: 10,
|
||||
child: FloatingActionButton(
|
||||
mini: true,
|
||||
backgroundColor: Colors.white,
|
||||
child: Icon(Icons.arrow_back, color: Colors.black),
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
}
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class DeliveryInfoCard extends StatelessWidget {
|
||||
final String deliveryDate;
|
||||
final String tenantName;
|
||||
final String orderId;
|
||||
final String contactNumber;
|
||||
final VoidCallback onPhoneTap;
|
||||
final Color primaryColor;
|
||||
|
||||
const DeliveryInfoCard({
|
||||
super.key,
|
||||
required this.deliveryDate,
|
||||
required this.tenantName,
|
||||
required this.orderId,
|
||||
required this.contactNumber,
|
||||
required this.onPhoneTap,
|
||||
required this.primaryColor,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final date = DateFormat("yyyy-MM-dd").parse(deliveryDate);
|
||||
final dateTime = DateFormat("yyyy-MM-ddTHH:mm:ss").parse(deliveryDate);
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Colors.black12,
|
||||
blurRadius: 6,
|
||||
offset: Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: SizedBox(
|
||||
height: 100,
|
||||
child: Row(
|
||||
children: [
|
||||
/// LEFT SIDE: DATE
|
||||
Container(
|
||||
width: 80,
|
||||
decoration: BoxDecoration(
|
||||
color: primaryColor,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(12),
|
||||
bottomLeft: Radius.circular(12),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
DateFormat("dd").format(date),
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
DateFormat("MMM").format(date),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey[600],
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
DateFormat("hh:mm a").format(dateTime),
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 12),
|
||||
|
||||
/// RIGHT SIDE: DETAILS
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildIconText(Icons.person, tenantName, 15),
|
||||
_buildIconText(Icons.receipt, orderId, 13),
|
||||
InkWell(
|
||||
onTap: onPhoneTap,
|
||||
child: _buildIconText(Icons.phone, contactNumber, 13, isLink: true),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildIconText(IconData icon, String text, double fontSize, {bool isLink = false}) {
|
||||
return Row(
|
||||
children: [
|
||||
Icon(icon, color: Colors.black45, size: 18),
|
||||
const SizedBox(width: 6),
|
||||
Expanded(
|
||||
child: Text(
|
||||
text,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
color: isLink ? Colors.blue : Colors.black87,
|
||||
fontWeight: FontWeight.w500,
|
||||
decoration: isLink ? TextDecoration.underline : TextDecoration.none,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class DeliveryCard extends StatelessWidget {
|
||||
final dynamic data;
|
||||
final bool isFromSummary;
|
||||
|
||||
const DeliveryCard({super.key, required this.data, this.isFromSummary = false});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
margin: const EdgeInsets.all(12),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
|
||||
elevation: 0,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
/// Timeline: Pickup icon, dotted line, Drop icon
|
||||
SizedBox(
|
||||
height: 190, // <-- match the combined height of your content
|
||||
child: Column(
|
||||
children: [
|
||||
_circleIcon(FontAwesomeIcons.mapMarkerAlt as IconData, isFromSummary ? Colors.red : Colors.green),
|
||||
Expanded(child: _verticalDottedLine()),
|
||||
_circleIcon((isFromSummary ? FontAwesomeIcons.checkCircle : FontAwesomeIcons.mapMarkerAlt) as IconData, isFromSummary ? Colors.green : Colors.red),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
|
||||
/// Content Block
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildInfoBlock(
|
||||
label: "Pickup",
|
||||
name: data?.pickupcustomer ?? '',
|
||||
address: data?.pickupaddress ?? '',
|
||||
contact: data?.pickupcontactno ?? '',
|
||||
lat: data?.pickuplat,
|
||||
lon: data?.pickuplon,
|
||||
isPickup: true,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_buildInfoBlock(
|
||||
label: isFromSummary ? "Delivered" : "Drop",
|
||||
name: data?.deliverycustomer ?? '',
|
||||
address: data?.deliveryaddress ?? '',
|
||||
contact: data?.deliverycontactno ?? '',
|
||||
lat: data?.droplat,
|
||||
lon: data?.droplon,
|
||||
isPickup: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Widget _circleIcon(IconData icon, Color color) {
|
||||
return Container(
|
||||
height: 38,
|
||||
width: 38,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.white,
|
||||
boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 3)],
|
||||
),
|
||||
child: FaIcon(icon as FaIconData?, color: color, size: 20),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _verticalDottedLine({double height = 60}) {
|
||||
return SizedBox(
|
||||
height: height,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: List.generate(
|
||||
(height ~/ 6),
|
||||
(index) => Container(
|
||||
width: 1,
|
||||
height: 4,
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInfoBlock({
|
||||
required String label,
|
||||
required String name,
|
||||
required String address,
|
||||
required String contact,
|
||||
required String? lat,
|
||||
required String? lon,
|
||||
required bool isPickup,
|
||||
}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
_infoRow(Icons.person, name),
|
||||
const SizedBox(height: 4),
|
||||
_infoRow(Icons.location_on, address, maxLines: 2),
|
||||
const SizedBox(height: 4),
|
||||
InkWell(
|
||||
onTap: () => Utility.openPhoneCallApp(contact),
|
||||
child: _infoRow(Icons.phone, contact),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Utility.openMap(
|
||||
double.tryParse(lat ?? '0') ?? 0,
|
||||
double.tryParse(lon ?? '0') ?? 0,
|
||||
);
|
||||
},
|
||||
child: const Row(
|
||||
children: [
|
||||
Icon(Icons.assistant_direction_rounded,
|
||||
color: ColorConstants.primaryColor, size: 20),
|
||||
SizedBox(width: 6),
|
||||
Text(
|
||||
"Navigate",
|
||||
style: TextStyle(
|
||||
color: ColorConstants.primaryColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _infoRow(IconData icon, String text, {int maxLines = 1}) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Icon(icon, size: 16, color: Colors.black45),
|
||||
const SizedBox(width: 6),
|
||||
Expanded(
|
||||
child: Text(
|
||||
text,
|
||||
maxLines: maxLines,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
color: Colors.black87,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user