master
Sneha 2 months ago
parent 8ebdc2b505
commit 37a5d3ce83

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 927 B

Before

Width:  |  Height:  |  Size: 945 B

After

Width:  |  Height:  |  Size: 945 B

@ -153,10 +153,11 @@ class AppSettings{
static String uploadPicUrl = host + 'uploads-user';
static String getOrderRequestsFromUsersUrl = host + 'getuserRequestbookingsforsupplier';
static String acceptOrderRequestsUrl = host + 'request-booking-with-charges';
static String getAcceptedOrdersFromUsersUrl = host + 'supplier/booking/advance-paid';
static String getAcceptedOrdersFromUsersUrl = host + 'getAllTankersBookingdetails';
static String getPlanRequestsFromUsersUrl = host + 'getuserRequestbookingsforplansforsupplier';
static String getTankersUrl = host + 'getTankers';
static String addTankerUrl = host + 'addTankers';
static String updateTankerUrl = host + 'updateTankers';
static String getDriversUrl = host + 'getalldeliveryboys';
static String addDriversUrl = host + 'addDeliveryboys';
static String addSourceLocationsUrl = host + 'addSource';
@ -164,7 +165,7 @@ class AppSettings{
static String setRatesDailyUrl = host + 'tankers';
static String getSupplierDetailsUrl = host + 'suppliers';
static String updatePumpFeeUrl = host + 'suppliers';
static String assignTankerUrl = host + 'suppliers';
static String assignTankerUrl = host + 'assign-deliveryboy-tanker';
@ -542,6 +543,40 @@ class AppSettings{
}
}
static Future<bool> updateTanker(payload,tankerName) async {
var uri = Uri.parse(updateTankerUrl+'/'+supplierId);
uri = uri.replace(query: 'tankerName=$tankerName');
var response = await http.put(uri,
body: json.encode(payload), headers: await buildRequestHeaders());
if (response.statusCode == 200) {
try {
var _response = json.decode(response.body);
print(_response);
return true;
} catch (e) {
// display error toast
return false;
}
} else if (response.statusCode == 401) {
bool status = await AppSettings.resetToken();
if (status) {
response = await http.put(uri,
body: json.encode(payload), headers: await buildRequestHeaders());
if (response.statusCode == 200) {
return true;
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
}
static Future<String> getDrivers() async {
var uri = Uri.parse(getDriversUrl+'/'+supplierId);
//uri = uri.replace(query: 'supplierId=$supplierId');

@ -364,7 +364,7 @@ class _FinancialMainScreenState extends State<FinancialMainScreen>
),
Image.asset("images/icon_tune.png", width: 24, height: 24),
const SizedBox(width: 16),
Image.asset("images/up_down arrow.png", width: 24, height: 24),
Image.asset("images/up_down_arrow.png", width: 24, height: 24),
],
),
),

@ -367,7 +367,7 @@ class _AcceptOrderRequestsState extends State<AcceptOrderRequests> {
payload["supplierId"] = AppSettings.supplierId;
payload["amount"] = int.parse(widget.order.quoted_amount);
payload["delivery_charges"] = advance;
payload["action"] = 'reject';
payload["action"] = 'rejected';
bool status = await AppSettings.acceptOrderRequests(
payload, widget.order.dbId);
@ -424,7 +424,7 @@ class _AcceptOrderRequestsState extends State<AcceptOrderRequests> {
payload["supplierId"] = AppSettings.supplierId;
payload["amount"] = int.parse(widget.order.quoted_amount);
payload["delivery_charges"] = advance;
payload["action"] = 'accept';
payload["action"] = 'accepted';
bool status = await AppSettings.acceptOrderRequests(
payload, widget.order.dbId);

@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:supplier_new/common/settings.dart';
import 'package:supplier_new/orders/delivery_updates.dart';
import 'package:supplier_new/orders/order_requests.dart';
import 'package:supplier_new/orders/orders_model.dart';
import 'package:supplier_new/orders/search_order_appbar.dart';
@ -9,6 +10,7 @@ import 'package:intl/intl.dart';
import 'assign_driver.dart';
import 'cancel_order.dart';
import 'change_driver.dart';
class AllOrders extends StatefulWidget {
final String navigationFrom;
@ -294,29 +296,42 @@ class OrderCard extends StatelessWidget {
const OrderCard({super.key, required this.order, this.onRefresh});
Color _getStatusColor() {
switch (order.status.toLowerCase()) {
String st='';
if(order.status.toLowerCase()=='advance_paid'){
st='pending';
}
else{
st=order.status.toLowerCase();
}
switch (st.toLowerCase()) {
case "completed":
return Color(0XFFC4E8C3);
case "in-progress":
return Color(0XFFE8F2FF);
case "in_progress":
return Color(0XFFC9DFFE);
case "cancelled":
return Color(0XFFFCEDEC);
case "assigned":
return Color(0XFFF9DBC6);
case "pending":
return Color(0XFFFDF3D3);
case "advance_paid":
return Color(0XFFFDF3D3);
default:
return Colors.grey;
}
}
Color _getTextStatusColor() {
switch (order.status.toLowerCase()) {
String st='';
if(order.status.toLowerCase()=='advance_paid'){
st='pending';
}
else{
st=order.status.toLowerCase();
}
switch (st.toLowerCase()) {
case "completed":
return Color(0XFF0A9E04);
case "in-progress":
case "in_progress":
return Color(0XFF1D7AFC);
case "cancelled":
return Color(0XFFE2483D);
@ -324,8 +339,6 @@ class OrderCard extends StatelessWidget {
return Color(0XFFE56910);
case "pending":
return Color(0XFFD0AE3C);
case "advance_paid":
return Color(0XFFD0AE3C);
default:
return Colors.grey;
}
@ -336,7 +349,10 @@ class OrderCard extends StatelessWidget {
final statusColor = _getStatusColor();
final textStatusColor = _getTextStatusColor();
return Padding(padding: EdgeInsets.fromLTRB(0, 8, 0, 0),
String st = (order.status == 'advance_paid') ? 'pending' : order.status;
return Padding(
padding: const EdgeInsets.fromLTRB(0, 8, 0, 0),
child: Container(
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
@ -350,31 +366,33 @@ class OrderCard extends StatelessWidget {
)
],
),
/// 👇 IntrinsicHeight allows both sides of the row to match height dynamically
child: IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch, // 👈 image stretches
children: [
// Image
/// 🖼 Left Image
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(12),
bottomLeft: Radius.circular(0),
bottomLeft: Radius.circular(12),
),
child: order.imageAsset != ''
child: SizedBox(
width: 145, // fixed width
child: order.imageAsset.isNotEmpty
? Image.asset(
order.imageAsset,
height: 145,
width: 145,
fit: BoxFit.cover,
fit: BoxFit.cover, // 👈 fills height
)
: Image.network(
order.imageAsset,
height: 100,
width: 100,
fit: BoxFit.cover,
),
),
),
// Right side content
/// 📄 Right Content
Expanded(
child: Padding(
padding: const EdgeInsets.all(10.0),
@ -392,48 +410,50 @@ class OrderCard extends StatelessWidget {
borderRadius: BorderRadius.circular(4),
),
child: Text(
order.status,
style: fontTextStyle(10,textStatusColor,FontWeight.w400)
st,
style: fontTextStyle(10, textStatusColor, FontWeight.w400),
),
),
const SizedBox(height: 6),
Text(
order.building_name,
style:fontTextStyle(16,Color(0XFF2D2E30),FontWeight.w600)
style: fontTextStyle(16, const Color(0XFF2D2E30), FontWeight.w600),
),
Text(
order.displayAddress,
style: fontTextStyle(12,Color(0XFF646566),FontWeight.w400)
style: fontTextStyle(12, const Color(0XFF646566), FontWeight.w400),
),
const SizedBox(height: 4),
Text(
order.capacity+' - '+order.type_of_water,
style: fontTextStyle(14,Color(0XFF444444),FontWeight.w500)
'${order.capacity} - ${order.type_of_water}',
style: fontTextStyle(14, const Color(0XFF444444), FontWeight.w500),
),
Text(
order.time+' , '+order.date,
style:fontTextStyle(8,Color(0XFF646566),FontWeight.w400)
'${order.time} , ${order.date}',
style: fontTextStyle(8, const Color(0XFF646566), FontWeight.w400),
),
const SizedBox(height: 12),
Row(
/// ===================
/// 🟡 ASSIGN & CANCEL BUTTONS
/// ===================
Visibility(
visible: order.status.toLowerCase() == 'advance_paid',
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Visibility(
visible: order.status.toLowerCase().toString()=='advance_paid',
child: GestureDetector(
onTap: ()async{
GestureDetector(
onTap: () async {
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AssignDriverScreen(order: order),
),
);
// If result indicates API reload
if (result == true) {
onRefresh?.call(); // 👈 safe call to refresh parent
onRefresh?.call();
}
},
child: Container(
@ -441,66 +461,177 @@ class OrderCard extends StatelessWidget {
borderRadius: BorderRadius.circular(22),
border: Border.all(color: const Color(0XFF939495)),
),
child: Padding(
padding: EdgeInsets.fromLTRB(8,4,8,4),
child: const Padding(
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
child: Text(
"Assign",
style: fontTextStyle(
14, const Color(0XFF515253), FontWeight.w400),
style: TextStyle(
fontSize: 14,
color: Color(0XFF515253),
fontWeight: FontWeight.w400),
),
),
),
),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DeliveryUpdatesPage(
orderId: order.dbId,
initialStatus: order.status,
),
),
);
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(22),
border: Border.all(color: const Color(0XFFE2483D)),
color: const Color(0XFFE2483D),
),
child: const Padding(
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
child: Text(
"Cancel",
style: TextStyle(
fontSize: 14,
color: Colors.white,
fontWeight: FontWeight.w400),
),
)
),
),),
),
),
],
),
),
/// ===================
/// 🟣 TRACK DELIVERY BUTTON
/// ===================
Visibility(
visible: order.status.toLowerCase().toString()=='advance_paid',
visible: order.status.toLowerCase() == 'in_progress',
child: GestureDetector(
onTap: ()async{
onTap: () async {
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CancelOrderScreen(order: order),
builder: (context) => AssignDriverScreen(order: order),
),
);
// If result indicates API reload
if (result == true) {
onRefresh?.call(); // 👈 safe call to refresh parent
onRefresh?.call();
}
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(22),
border: Border.all(color: const Color(0XFFE2483D)),
color: Color(0XFFE2483D)
color: const Color(0XFF8270DB),
border: Border.all(color: const Color(0XFF8270DB)),
),
child: Padding(
padding: EdgeInsets.fromLTRB(8,4,8,4),
child: const Padding(
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
child: Text(
"Cancel",
"Track Delivery",
style: TextStyle(
fontSize: 14,
color: Colors.white,
fontWeight: FontWeight.w400),
),
),
),
),
),
/// ===================
/// 🟣 TRACK DELIVERY BUTTON
/// ===================
Visibility(
visible: order.status.toLowerCase() == 'assigned',
/* child: */
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.asset(
'images/avatar.png',
fit: BoxFit.cover,
width: 12,
height: 12,
),
const SizedBox(width: 8),
// Title + Chips
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
"Assigned to ${order.delivery_agent_name}",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: fontTextStyle(
14, const Color(0XFFFFFFFF), FontWeight.w400),
8, const Color(0xFF646566), FontWeight.w400),
),
)
),
),),
],
)
),
],
),
),
/*Text(
order.extraInfo,
style:fontTextStyle(12, order.status == "in-progress"
? Color(0XFF1D7AFC)
: Color(0XFF646566),FontWeight.w400)
],
),
SizedBox(height: MediaQuery.of(context).size.height * .004),
GestureDetector(
onTap: () async {
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChangeDriverScreen(order: order),
),
);
if (result == true) {
onRefresh?.call();
}
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(22),
color: const Color(0XFF8270DB),
border: Border.all(color: const Color(0XFF8270DB)),
),
child: const Padding(
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
child: Text(
"Change",
style: TextStyle(
fontSize: 14,
color: Colors.white,
fontWeight: FontWeight.w400),
),
),
),
),
],
)
),
),*/
],
),
),
),
],
),
),);
),
),
);
}
}

@ -129,8 +129,8 @@ class _AssignDriverScreenState extends State<AssignDriverScreen> {
children: [
Text(
"Assign Tanker",
style: fontTextStyle(
16, const Color(0XFF2A2A2A), FontWeight.w600),
style: fontTextStyle(16,
const Color(0XFF2A2A2A), FontWeight.w600),
),
GestureDetector(
onTap: () => Navigator.pop(context),
@ -157,7 +157,8 @@ class _AssignDriverScreenState extends State<AssignDriverScreen> {
child: Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
widget.order.building_name,
@ -197,7 +198,8 @@ class _AssignDriverScreenState extends State<AssignDriverScreen> {
const SizedBox(height: 12),
isTankersDataLoading
? const Center(child: CircularProgressIndicator())
? const Center(
child: CircularProgressIndicator())
: (tankersList.isEmpty
? Center(
child: Text(
@ -210,12 +212,14 @@ class _AssignDriverScreenState extends State<AssignDriverScreen> {
)
: ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
physics:
const NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: tankersList
.where((t) =>
_capToLiters(t.capacity) ==
_capToLiters(widget.order.capacity))
_capToLiters(
widget.order.capacity))
.length,
separatorBuilder: (_, __) =>
const SizedBox(height: 12),
@ -223,7 +227,8 @@ class _AssignDriverScreenState extends State<AssignDriverScreen> {
final filteredTankers = tankersList
.where((t) =>
_capToLiters(t.capacity) ==
_capToLiters(widget.order.capacity))
_capToLiters(
widget.order.capacity))
.toList();
final d = filteredTankers[idx];
final isSelected =
@ -233,7 +238,8 @@ class _AssignDriverScreenState extends State<AssignDriverScreen> {
onTap: () {
setModalState(() {
selectedTankerIndex = idx;
selectedDriverIndex = null; // reset driver selection if tanker changes
selectedDriverIndex =
null; // reset driver selection if tanker changes
});
},
child: Card(
@ -280,22 +286,28 @@ class _AssignDriverScreenState extends State<AssignDriverScreen> {
decoration: BoxDecoration(
color: const Color(0XFFFFFFFF),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFFC3C4C4)),
border: Border.all(
color: const Color(0xFFC3C4C4)),
),
child: Center(
child: Text(
'Select a tanker to choose driver',
style: fontTextStyle(
14, const Color(0xFF2D2E30), FontWeight.w400),
14,
const Color(0xFF2D2E30),
FontWeight.w400),
),
),
)
: isLoading
? const Center(child: CircularProgressIndicator())
? const Center(
child: CircularProgressIndicator())
: (driversList.isEmpty
? Center(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
padding:
const EdgeInsets.symmetric(
vertical: 12),
child: Text(
'No Data Available',
style: fontTextStyle(
@ -307,21 +319,26 @@ class _AssignDriverScreenState extends State<AssignDriverScreen> {
)
: ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
physics:
const NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: driversList.length,
separatorBuilder: (_, __) => const SizedBox(height: 12),
separatorBuilder: (_, __) =>
const SizedBox(height: 12),
itemBuilder: (context, idx) {
final d = driversList[idx];
final isSelected = selectedDriverIndex == idx;
final isAvailable = d.status == "available";
final isSelected =
selectedDriverIndex == idx;
final isAvailable =
d.status == "available";
final statusColor = isAvailable
? const Color(0XFF0A9E04)
: (d.status == "on delivery"
? const Color(0XFFD0AE3C)
: (d.status == "offline"
? const Color(0XFF939495)
? const Color(
0XFF939495)
: Colors.grey));
return GestureDetector(
@ -340,17 +357,25 @@ class _AssignDriverScreenState extends State<AssignDriverScreen> {
});
},
child: Card(
color: const Color(0XFFFFFFFF),
color:
const Color(0XFFFFFFFF),
elevation: 1,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
borderRadius:
BorderRadius.circular(
12),
side: BorderSide(
color: isSelected ? primaryColor : const Color(0XFFC3C4C4),
color: isSelected
? primaryColor
: const Color(
0XFFC3C4C4),
width: 1,
),
),
child: Padding(
padding: const EdgeInsets.all(12.0),
padding:
const EdgeInsets.all(
12.0),
child: Row(
children: [
Image.asset(
@ -359,24 +384,45 @@ class _AssignDriverScreenState extends State<AssignDriverScreen> {
width: 20,
height: 20,
),
const SizedBox(width: 8),
const SizedBox(
width: 8),
Expanded(
child: Text(
d.driver_name,
style: fontTextStyle(
14, const Color(0XFF2D2E30), FontWeight.w500),
14,
const Color(
0XFF2D2E30),
FontWeight
.w500),
),
),
const SizedBox(width: 8),
const SizedBox(
width: 8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
border: Border.all(color: statusColor),
padding:
const EdgeInsets
.symmetric(
horizontal: 6,
vertical: 2),
decoration:
BoxDecoration(
borderRadius:
BorderRadius
.circular(
4),
border: Border.all(
color:
statusColor),
),
child: Text(
d.status,
style: fontTextStyle(10, statusColor, FontWeight.w400),
style:
fontTextStyle(
10,
statusColor,
FontWeight
.w400),
),
),
],
@ -395,20 +441,17 @@ class _AssignDriverScreenState extends State<AssignDriverScreen> {
SafeArea(
top: false,
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 12),
child: SizedBox(
width: double.infinity,
child: GestureDetector(
onTap: () async {
if (selectedTankerIndex == null) {
AppSettings.longFailedToast('Please select tanker');
AppSettings.longFailedToast(
'Please select tanker');
return;
}
/*if (selectedDriverIndex == null) {
AppSettings.longFailedToast('Please select driver');
return;
}*/
final filteredTankers = tankersList
.where((t) =>
@ -416,55 +459,55 @@ class _AssignDriverScreenState extends State<AssignDriverScreen> {
_capToLiters(widget.order.capacity))
.toList();
final selectedTanker = filteredTankers[selectedTankerIndex!];
final selectedDriver = driversList[selectedDriverIndex!];
final selectedTanker =
filteredTankers[selectedTankerIndex!];
final selectedDriver = selectedDriverIndex != null
? driversList[selectedDriverIndex!]
: null;
AppSettings.preLoaderDialog(context);
bool isOnline = await AppSettings.internetConnectivity();
bool isOnline =
await AppSettings.internetConnectivity();
if (isOnline) {
var payload = new Map<String, dynamic>();
payload["tankerName"] = selectedTanker.tanker_name;
if(selectedDriverIndex != null){
payload["delivery_agent"] = selectedDriver.driver_name;
payload["delivery_agent_mobile"] = selectedDriver.phone_number;
}
else{
payload["delivery_agent"] =null;
payload["delivery_agent_mobile"] = null;
}
var payload = <String, dynamic>{};
payload["tankerName"] =
selectedTanker.tanker_name;
payload["delivery_agent"] =
selectedDriver?.driver_name;
payload["delivery_agent_mobile"] =
selectedDriver?.phone_number;
bool status = await AppSettings.assignTanker(
payload, widget.order.dbId);
bool status = await AppSettings.assignTanker(payload, widget.order.dbId);
Navigator.of(context, rootNavigator: true)
.pop(); // close loader
try {
if (status) {
Navigator.of(context,rootNavigator: true).pop();
AppSettings.longFailedToast("Tanker assigned successfully");
Navigator.pop(context, true);
AppSettings.longSuccessToast(
"Tanker assigned successfully");
// 👇 Pop bottom sheet first
Navigator.pop(context);
// 👇 Then pop the AssignDriverScreen and send true to parent
Navigator.pop(context, true);
} else {
AppSettings.longFailedToast(
"Failed to assign tanker");
}
else{
Navigator.of(context,rootNavigator: true).pop();
AppSettings.longFailedToast("Failed to assign tanker");
}
} catch (e) {
Navigator.of(context,rootNavigator: true).pop();
print(e);
}
}
else{
Navigator.of(context,rootNavigator: true).pop();
AppSettings.longFailedToast("Please Check internet");
} else {
Navigator.of(context, rootNavigator: true)
.pop();
AppSettings.longFailedToast(
"Please Check internet");
}
if (context.mounted) Navigator.pop(context);
},
child: Container(
decoration: BoxDecoration(
color: const Color(0XFF8270DB),
borderRadius: BorderRadius.circular(24),
borderRadius : BorderRadius.circular(24),
),
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(vertical: 12),
@ -954,7 +997,8 @@ class TankersCard extends StatelessWidget {
borderRadius: BorderRadius.circular(8),
border: Border.all(color: chipTextColor, width: 1),
),
child: Text(s, style: fontTextStyle(10, chipTextColor, FontWeight.w400)),
child:
Text(s, style: fontTextStyle(10, chipTextColor, FontWeight.w400)),
);
}
@ -991,7 +1035,8 @@ class TankersCard extends StatelessWidget {
"$subtitle$title",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: fontTextStyle(14, const Color(0xFF2D2E30), FontWeight.w500),
style: fontTextStyle(
14, const Color(0xFF2D2E30), FontWeight.w500),
),
),
const SizedBox(width: 8),
@ -1009,7 +1054,8 @@ class TankersCard extends StatelessWidget {
),
Text(
code,
style: fontTextStyle(10, const Color(0xFF646566), FontWeight.w400),
style: fontTextStyle(
10, const Color(0xFF646566), FontWeight.w400),
),
],
),
@ -1019,6 +1065,5 @@ class TankersCard extends StatelessWidget {
],
),
);
}
}

@ -1,10 +1,8 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:supplier_new/common/settings.dart';
import 'package:supplier_new/orders/edit_order_requests.dart';
import '../resources/drivers_model.dart';
import '../resources/tankers_model.dart';
class ChangeDriverScreen extends StatefulWidget {
var order;
@ -20,12 +18,18 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
double amountToPayAfterDelivery = 0.0;
double totalFare = 0.0;
bool isLoading = false;
bool isTankersDataLoading = false;
List<DriversModel> driversList = [];
List<TankersModel> tankersList = [];
TankersModel? orderTanker;
DriversModel? orderDriver;
@override
void initState() {
// TODO: implement initState
super.initState();
_fetchTankers();
_fetchDrivers();
advance = 150;
advancePayable = advance;
@ -41,8 +45,21 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
.map((e) => DriversModel.fromJson(e))
.toList();
if (!mounted) return;
// 🔎 Match driver by name (case-insensitive, trimmed)
final wanted = (widget.order.delivery_agent_name ?? '').toString().trim().toLowerCase();
DriversModel? matched;
for (final d in data) {
final name = (d.driver_name ?? '').toString().trim().toLowerCase();
if (name == wanted) {
matched = d;
break;
}
}
setState(() {
driversList = data;
orderDriver = matched; // store matched driver or null
isLoading = false;
});
} catch (e) {
@ -51,34 +68,319 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
}
}
void _showAssignDriverBottomSheet() {
int? selectedDriverIndex; // Track selected driver
Future<void> _fetchTankers() async {
setState(() => isTankersDataLoading = true);
try {
final response = await AppSettings.getTankers();
final data = (jsonDecode(response)['data'] as List)
.map((e) => TankersModel.fromJson(e))
.toList();
if (!mounted) return;
// 🔎 Find tanker by name (case-insensitive, trimmed)
final wanted = (widget.order.tanker_name ?? '').toString().trim().toLowerCase();
TankersModel? matched;
for (final t in data) {
final name = (t.tanker_name ?? '').toString().trim().toLowerCase();
if (name == wanted) {
matched = t;
break;
}
}
setState(() {
tankersList = data;
orderTanker = matched; // <- store the matched tanker (or null if not found)
isTankersDataLoading = false;
});
} catch (e) {
debugPrint("⚠️ Error fetching tankers: $e");
setState(() => isTankersDataLoading = false);
}
}
Widget _assignedTankerDetails() {
if (isTankersDataLoading) {
return const Center(child: CircularProgressIndicator());
}
// If not found, show a simple fallback using just the name from order
if (orderTanker == null) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0XFFFFFFFF),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0XFFFFFFFF)),
),
child: Row(
children: [
Image.asset('images/square_avatar.png', width: 24, height: 24),
const SizedBox(width: 8),
Expanded(
child: Text(
widget.order.tanker_name ?? "Tanker",
style: fontTextStyle(14, const Color(0xFF2D2E30), FontWeight.w500),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
// No details available
],
),
);
}
// Found: show full details from fetched TankersModel
final t = orderTanker!;
final availability = (t.availability ?? []).cast<String>();
String? primaryStatus;
const pref = ['in-use', 'available', 'filled', 'empty', 'maintenance'];
for (final s in pref) {
if (availability.any((x) => x.toLowerCase() == s)) { primaryStatus = s; break; }
}
primaryStatus ??= availability.isNotEmpty ? availability.first : null;
Color _statusTextColor(String s) {
switch (s.toLowerCase()) {
case 'filled': return const Color(0xFF1D7AFC);
case 'available': return const Color(0xFF0A9E04);
case 'empty': return const Color(0xFFE2483D);
case 'in-use': return const Color(0xFFEA843B);
case 'maintenance': return const Color(0xFFD0AE3C);
default: return const Color(0xFF2A2A2A);
}
}
Widget _statusChip(String label) {
final c = _statusTextColor(label);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(8),
border: Border.all(color: c, width: 1),
),
child: Text(label, style: fontTextStyle(10, c, FontWeight.w400)),
);
}
Widget _line(String title, String value) {
return Padding(
padding: const EdgeInsets.only(top: 6),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(title, style: fontTextStyle(12, const Color(0xFF646566), FontWeight.w400)),
Flexible(
child: Text(value,
style: fontTextStyle(12, const Color(0xFF2D2E30), FontWeight.w500),
textAlign: TextAlign.right, overflow: TextOverflow.ellipsis, maxLines: 1),
),
],
),
);
}
return Container(
padding: const EdgeInsets.all(0),
decoration: BoxDecoration(
color: const Color(0XFFFFFFFF),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0XFFFFFFFF)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Image.asset('images/avatar.png', width: 24, height: 24),
const SizedBox(width: 8),
Expanded(
child: Text(
t.tanker_name ?? "",
style: fontTextStyle(14, const Color(0xFF2D2E30), FontWeight.w500),
maxLines: 1, overflow: TextOverflow.ellipsis,
),
),
if (primaryStatus != null) _statusChip(primaryStatus),
],
),
const SizedBox(height: 8),
Visibility(
visible:(t.license_plate!="") ,
child: Text(
t.license_plate ?? "",
style: fontTextStyle(12, const Color(0XFF646566), FontWeight.w400),
maxLines: 1, overflow: TextOverflow.ellipsis,
),)
/* _line("License Plate", t.license_plate ?? ""),
_line("Water Type", t.type_of_water ?? ""),
_line("Capacity", t.capacity ?? ""),
if ((t.supplier_name ?? "").isNotEmpty) _line("Owner", t.supplier_name),*/
],
),
);
}
Widget _assignedDriverDetails() {
if (isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (orderDriver == null) {
// fallback if not found
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0XFFFFFFFF),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0XFFC3C4C4)),
),
child: Row(
children: [
Image.asset('images/avatar.png', width: 24, height: 24),
const SizedBox(width: 8),
Expanded(
child: Text(
widget.order.delivery_agent_name ?? "Driver",
style: fontTextStyle(14, const Color(0XFF2D2E30), FontWeight.w500),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
);
}
final d = orderDriver!;
Color _statusTextColor(String s) {
switch (s.toLowerCase()) {
case 'available': return const Color(0XFF0A9E04);
case 'on delivery': return const Color(0XFFD0AE3C);
case 'offline': return const Color(0XFF939495);
default: return greyColor;
}
}
Widget _statusChip(String label) {
final c = _statusTextColor(label);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: c, width: 1),
),
child: Text(label, style: fontTextStyle(10, c, FontWeight.w400)),
);
}
Widget _line(String title, String value) {
return Padding(
padding: const EdgeInsets.only(top: 6),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(title, style: fontTextStyle(12, const Color(0xFF646566), FontWeight.w400)),
Flexible(
child: Text(
value,
style: fontTextStyle(12, const Color(0xFF2D2E30), FontWeight.w500),
textAlign: TextAlign.right,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
],
),
);
}
return Container(
padding: const EdgeInsets.all(0),
decoration: BoxDecoration(
color: const Color(0XFFFFFFFF),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0XFFFFFFFF)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Image.asset('images/avatar.png', width: 24, height: 24),
const SizedBox(width: 8),
Expanded(
child: Text(
d.driver_name ?? "",
style: fontTextStyle(14, const Color(0xFF2D2E30), FontWeight.w500),
maxLines: 1, overflow: TextOverflow.ellipsis,
),
),
if ((d.status ?? "").isNotEmpty) _statusChip(d.status),
],
),
/* const SizedBox(height: 8),
_line("Phone", d.phone_number ?? ""),*/
],
),
);
}
void _showAssignTankerBottomSheet() {
// 🔸 Find default selected indexes
int? selectedTankerIndex;
int? selectedDriverIndex;
int _capToLiters(dynamic cap) {
if (cap == null) return -1;
if (cap is num) return cap.round();
final s = cap.toString().toLowerCase().replaceAll(',', '').trim();
final match = RegExp(r'(\d+(\.\d+)?)').firstMatch(s);
if (match == null) return -1;
final n = double.tryParse(match.group(1)!) ?? -1;
if (n < 0) return -1;
if (s.contains('kl')) return (n * 1000).round();
return n.round();
}
// 🧠 Preselect assigned tanker
final filteredTankers = tankersList
.where((t) => _capToLiters(t.capacity) == _capToLiters(widget.order.capacity))
.toList();
selectedTankerIndex = filteredTankers.indexWhere(
(t) => t.tanker_name == widget.order.tanker_name);
// 🧠 Preselect assigned driver
selectedDriverIndex = driversList.indexWhere(
(d) => d.driver_name == widget.order.delivery_agent_name);
showModalBottomSheet(
backgroundColor: const Color(0XFFFFFFFF),
context: context,
isScrollControlled: true,
isDismissible: false,
enableDrag: false,
enableDrag: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
builder: (context) {
return WillPopScope( // block Android back button
return WillPopScope(
onWillPop: () async => false,
child: StatefulBuilder(
builder: (context, setModalState) {
return DraggableScrollableSheet(
expand: false,
initialChildSize: 0.7,
minChildSize: 0.5,
maxChildSize: 0.9,
builder: (context, scrollController) {
return Container(
return FractionallySizedBox(
heightFactor: 0.95,
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -97,73 +399,132 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Assign Driver",
style: fontTextStyle(16, const Color(0XFF2A2A2A), FontWeight.w600),
"Edit Order",
style: fontTextStyle(16,
const Color(0XFF2A2A2A), FontWeight.w600),
),
GestureDetector(
onTap: (){
Navigator.pop(context);
},
child: Image.asset('images/cross.png', height: 24, width: 24,color: Color(0XFF2A2A2A),),
onTap: () => Navigator.pop(context),
child: Image.asset(
'images/cross.png',
height: 24,
width: 24,
color: const Color(0XFF2A2A2A),
),
)
],
),
const SizedBox(height: 16),
// 🏢 Order Info
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Colors.white,
border: Border.all(color: const Color(0XFFC9C2F0), width: 0.5),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// 🛻 Tanker List
Text(
widget.order.building_name,
style: fontTextStyle(20, const Color(0XFF2D2E30), FontWeight.w600),
"SELECTED TANKER",
style: fontTextStyle(
10, const Color(0XFF2D2E30), FontWeight.w600),
),
Text(
widget.order.displayAddress,
style: fontTextStyle(12, const Color(0XFF939495), FontWeight.w400),
const SizedBox(height: 12),
isTankersDataLoading
? const Center(child: CircularProgressIndicator())
: (filteredTankers.isEmpty
? Center(
child: Text(
'No Data Available For Capacity ${widget.order.capacity}',
style: fontTextStyle(
12,
const Color(0xFF939495),
FontWeight.w500),
),
],
)
: ListView.separated(
shrinkWrap: true,
physics:
const NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: filteredTankers.length,
separatorBuilder: (_, __) =>
const SizedBox(height: 12),
itemBuilder: (context, idx) {
final t = filteredTankers[idx];
final isSelected =
selectedTankerIndex == idx;
return GestureDetector(
onTap: () {
setModalState(() {
selectedTankerIndex = idx;
selectedDriverIndex = null;
});
},
child: Card(
elevation: 1,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(12),
side: BorderSide(
color: isSelected
? primaryColor
: const Color(0XFFC3C4C4),
width: 1,
),
const Spacer(),
Text(
'${widget.order.distanceInKm} Km',
style: fontTextStyle(12, const Color(0XFF939495), FontWeight.w400),
),
],
child: TankersCard(
title: t.tanker_name,
subtitle: t.type_of_water,
capacity: t.capacity,
code: t.license_plate,
owner: t.supplier_name,
status: List<String>.from(
t.availability),
),
),
);
},
)),
const SizedBox(height: 16),
/// 🧍 Driver List
Text(
"SELECT DRIVER",
style: fontTextStyle(10, const Color(0XFF2D2E30), FontWeight.w600),
"SELECTED DRIVER",
style: fontTextStyle(
10, const Color(0XFF2D2E30), FontWeight.w600),
),
const SizedBox(height: 12),
// 🧍 Driver List
Expanded(
child: isLoading
? const Center(child: CircularProgressIndicator())
selectedTankerIndex == null
? Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
margin: const EdgeInsets.only(top: 8),
decoration: BoxDecoration(
color: const Color(0XFFFFFFFF),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: const Color(0xFFC3C4C4)),
),
child: Center(
child: Text(
'Select a tanker to choose driver',
style: fontTextStyle(
14,
const Color(0xFF2D2E30),
FontWeight.w400),
),
),
)
: ListView.separated(
controller: scrollController,
shrinkWrap: true,
physics:
const NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: driversList.length,
separatorBuilder: (_, __) => const SizedBox(height: 12),
separatorBuilder: (_, __) =>
const SizedBox(height: 12),
itemBuilder: (context, idx) {
final d = driversList[idx];
final bool isSelected = selectedDriverIndex == idx;
final bool isAvailable = d.status == "available";
final statusColor = isAvailable
final isSelected =
selectedDriverIndex == idx;
final statusColor = d.status == "available"
? const Color(0XFF0A9E04)
: (d.status == "on delivery"
? const Color(0XFFD0AE3C)
@ -173,31 +534,23 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
return GestureDetector(
onTap: () {
if (isAvailable) { // only selectable if available
setModalState(() {
selectedDriverIndex = idx;
});
} else {
AppSettings.longFailedToast(
'Only available drivers can be selected',
);
}
},
child: Opacity(
opacity: isAvailable ? 1 : 1, // 👈 grey out non-available
child: Card(
color: const Color(0XFFFFFFFF),
elevation: 1,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
borderRadius:
BorderRadius.circular(12),
side: BorderSide(
color: isSelected
? const Color(0XFF8270DB)
? primaryColor
: const Color(0XFFC3C4C4),
width: 1,
),
),
color: isSelected
? const Color(0XFFEDEBFF)
: Colors.white,
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Row(
@ -208,64 +561,103 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
width: 20,
height: 20,
),
SizedBox(
width: MediaQuery.of(context).size.width * .016),
const SizedBox(width: 8),
Expanded(
child: Text(
d.driver_name,
style: fontTextStyle(
14,
const Color(0XFF2D2E30),
FontWeight.w500,
),
const Color(
0XFF2D2E30),
FontWeight.w500),
),
),
SizedBox(
width: MediaQuery.of(context).size.width * .016),
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 6, vertical: 2),
padding:
const EdgeInsets.symmetric(
horizontal: 6,
vertical: 2),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
border: Border.all(color: statusColor),
borderRadius:
BorderRadius.circular(4),
border: Border.all(
color: statusColor),
),
child: Text(
d.status,
style: fontTextStyle(
10,
statusColor,
FontWeight.w400,
),
FontWeight.w400),
),
),
],
),
),
),
),
);
},
),
],
),
),
),
const SizedBox(height: 16),
// 🟣 Assign Button
SizedBox(
/// Assign Button
SafeArea(
top: false,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 12),
child: SizedBox(
width: double.infinity,
child: GestureDetector(
onTap: () async {
if (selectedDriverIndex == null) {
AppSettings.longFailedToast('Please select driver');
if (selectedTankerIndex == null) {
AppSettings.longFailedToast(
'Please select tanker');
return;
}
final selectedDriver = driversList[selectedDriverIndex!];
final selectedTanker =
filteredTankers[selectedTankerIndex!];
final selectedDriver = selectedDriverIndex != null
? driversList[selectedDriverIndex!]
: null;
// Call your API here
// await _assignDriverApi(selectedDriver.driver_id);
AppSettings.preLoaderDialog(context);
bool isOnline =
await AppSettings.internetConnectivity();
if (context.mounted) Navigator.pop(context);
if (isOnline) {
var payload = <String, dynamic>{};
payload["tankerName"] =
selectedTanker.tanker_name;
payload["delivery_agent"] =
selectedDriver?.driver_name;
payload["delivery_agent_mobile"] =
selectedDriver?.phone_number;
bool status = await AppSettings.assignTanker(
payload, widget.order.dbId);
Navigator.of(context, rootNavigator: true).pop();
if (status) {
AppSettings.longSuccessToast(
"Tanker assigned successfully");
Navigator.pop(context);
Navigator.pop(context, true);
} else {
AppSettings.longFailedToast(
"Failed to assign tanker");
}
} else {
Navigator.of(context, rootNavigator: true).pop();
AppSettings.longFailedToast(
"Please Check internet");
}
},
child: Container(
decoration: BoxDecoration(
@ -277,7 +669,9 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
child: Text(
'Assign',
style: fontTextStyle(
14, const Color(0XFFFFFFFF), FontWeight.w500),
14, Colors.white, FontWeight.w500),
),
),
),
),
),
@ -286,20 +680,15 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
),
);
},
),
);
},
),);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
extendBodyBehindAppBar: true,
@ -523,42 +912,30 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
style: fontTextStyle(
12, const Color(0XFF646566), FontWeight.w400),
),
Image.asset(
'images/avatar.png',
fit: BoxFit.cover,
width: 20,
height: 20,
),
SizedBox(
width: MediaQuery.of(context).size.width * .016),
Expanded(
child: Text(
'd.driver_name',
style: fontTextStyle(
14,
const Color(0XFF2D2E30),
FontWeight.w500,
height: MediaQuery.of(context).size.height * .032,
),
Text(
"ASSIGNED TANKER",
style: fontTextStyle(
10, const Color(0XFF646566), FontWeight.w600),
),
SizedBox(
height: MediaQuery.of(context).size.height * .012,
),
_assignedTankerDetails(),
SizedBox(
width: MediaQuery.of(context).size.width * .016),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 6, vertical: 2),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
border: Border.all(color: Colors.green),
height: MediaQuery.of(context).size.height * .012,
),
child: Text(
'd.status',
Text(
"ASSIGNED TO",
style: fontTextStyle(
10,
Colors.green,
FontWeight.w400,
),
10, const Color(0XFF646566), FontWeight.w600),
),
SizedBox(
height: MediaQuery.of(context).size.height * .012,
),
_assignedDriverDetails()
],
)),
],
@ -583,7 +960,7 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
padding: EdgeInsets.symmetric(vertical: 10),
),
onPressed: () async {
AppSettings.preLoaderDialog(context);
/*AppSettings.preLoaderDialog(context);
bool isOnline = await AppSettings.internetConnectivity();
@ -615,7 +992,8 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
} else {
Navigator.of(context, rootNavigator: true).pop();
AppSettings.longFailedToast("Please Check internet");
}
}*/
Navigator.pop(context);
},
child: Text(
"CANCEL",
@ -633,10 +1011,10 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
padding: EdgeInsets.symmetric(vertical: 10),
),
onPressed: () async {
_showAssignDriverBottomSheet();
_showAssignTankerBottomSheet();
},
child: Text(
"Change Order",
"Edit Order",
style: fontTextStyle(
14, const Color(0XFFFFFFFF), FontWeight.w400),
),
@ -728,3 +1106,144 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
);
}
}
// ====== TankersCard ======
class TankersCard extends StatelessWidget {
final String title;
final String subtitle;
final String capacity;
final String code;
final String owner;
final List<String> status;
const TankersCard({
super.key,
required this.title,
required this.subtitle,
required this.capacity,
required this.code,
required this.owner,
required this.status,
});
Color _chipColor(String s) {
switch (s) {
case 'filled':
return const Color(0xFFFFFFFF);
case 'available':
return const Color(0xFFFFFFFF);
case 'empty':
return const Color(0xFFFFFFFF);
case 'in-use':
return const Color(0xFFFFFFFF);
case 'maintenance':
return const Color(0xFFFFFFFF);
default:
return const Color(0xFFFFFFFF);
}
}
Color _chipTextColor(String s) {
switch (s) {
case 'filled':
return const Color(0xFF1D7AFC);
case 'available':
return const Color(0xFF0A9E04);
case 'empty':
return const Color(0xFFE2483D);
case 'in-use':
return const Color(0xFFEA843B);
case 'maintenance':
return const Color(0xFFD0AE3C);
default:
return Color(0xFF2A2A2A);
}
}
@override
Widget build(BuildContext context) {
/*ImageProvider avatarProvider =
(AppSettings.profilePictureUrl != '' && AppSettings.profilePictureUrl != 'null')
? NetworkImage(AppSettings.profilePictureUrl)
: const AssetImage("images/profile_pic.png") as ImageProvider;*/
Widget _statusChip(String s) {
final chipTextColor = _chipTextColor(s);
return Container(
margin: const EdgeInsets.only(left: 6),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: _chipColor(s),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: chipTextColor, width: 1),
),
child:
Text(s, style: fontTextStyle(10, chipTextColor, FontWeight.w400)),
);
}
return Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: const Color(0XFFFFFFFF),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0XFFC3C4C4)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.asset(
'images/square_avatar.png',
fit: BoxFit.cover,
width: 24,
height: 24,
),
const SizedBox(width: 8),
// Title + Chips
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
"$subtitle$title",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: fontTextStyle(
14, const Color(0xFF2D2E30), FontWeight.w500),
),
),
const SizedBox(width: 8),
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 160),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
reverse: true,
child: Row(
children: status.map(_statusChip).toList(),
),
),
),
],
),
Text(
code,
style: fontTextStyle(
10, const Color(0xFF646566), FontWeight.w400),
),
],
),
),
],
),
],
),
);
}
}

@ -0,0 +1,131 @@
import 'package:flutter/material.dart';
class DeliveryUpdatesPage extends StatefulWidget {
final String orderId;
final String initialStatus;
const DeliveryUpdatesPage({super.key, required this.orderId, required this.initialStatus});
@override
State<DeliveryUpdatesPage> createState() => _DeliveryUpdatesPageState();
}
class _DeliveryUpdatesPageState extends State<DeliveryUpdatesPage> {
List<String> statuses = [
"Tanker reached source",
"Water filling started",
"Water filling completed",
"Tanker started to customer location",
"Offloading water started",
"Offloading water completed",
"Payment completed",
"Delivery completed"
];
int currentStep = 0;
@override
void initState() {
super.initState();
// Example: Get live updates from backend (MQTT, WebSocket, Firestore, etc.)
_simulateStatusUpdates();
}
void _simulateStatusUpdates() async {
// This is just simulation replace with your listener
for (int i = 0; i < statuses.length; i++) {
await Future.delayed(const Duration(seconds: 3));
setState(() {
currentStep = i;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Track Order"),
backgroundColor: const Color(0XFF0A9E04),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Expanded(
child: ListView.builder(
itemCount: statuses.length,
itemBuilder: (context, index) {
final isCompleted = index <= currentStep;
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
children: [
// Circle with tanker icon or checkmark
Container(
width: 30,
height: 30,
decoration: BoxDecoration(
color: isCompleted ? Colors.green : Colors.grey[300],
shape: BoxShape.circle,
),
child: Center(
child: index == currentStep
? const Icon(Icons.local_shipping)
: Icon(
isCompleted
? Icons.check
: Icons.circle_outlined,
size: 16,
color: isCompleted ? Colors.white : Colors.grey,
),
),
),
if (index != statuses.length - 1)
Container(
width: 4,
height: 50,
color: index < currentStep ? Colors.green : Colors.grey[300],
),
],
),
const SizedBox(width: 12),
Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 4),
child: Text(
statuses[index],
style: TextStyle(
fontSize: 14,
fontWeight: isCompleted ? FontWeight.w600 : FontWeight.w400,
color: isCompleted ? Colors.black : Colors.grey[600],
),
),
),
)
],
);
},
),
),
// 🔸 Current status shown at bottom
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.green.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Text(
"Current Status: ${statuses[currentStep]}",
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600),
),
),
],
),
),
);
}
}

@ -21,6 +21,8 @@ class OrdersModel {
String status='';
String date='';
String imageAsset='images/building.png';
String delivery_agent_name = '';
String tanker_name = '';
OrdersModel();
@ -39,6 +41,8 @@ class OrdersModel {
rtvm.quoted_amount = json['price'].toString() ?? '';
rtvm.lng=json['longitude'] ?? 0.0;
rtvm.lat=json['latitude'] ?? 0.0;
rtvm.delivery_agent_name = json['delivery_agent'] ?? '';
rtvm.tanker_name = json['tankerName'] ?? '';
// Split and trim
List<String> parts = rtvm.address.split(',').map((e) => e.trim()).toList();

@ -509,7 +509,7 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
const SizedBox(width: 16),
Image.asset("images/icon_tune.png", width: 24, height: 24),
const SizedBox(width: 16),
Image.asset("images/up_down arrow.png", width: 24, height: 24),
Image.asset("images/up_down_arrow.png", width: 24, height: 24),
],
),
const SizedBox(height: 12),

@ -36,7 +36,6 @@ class FirstCharUppercaseFormatter extends TextInputFormatter {
);
}
}
void main() => runApp(const MaterialApp(home: ResourcesFleetScreen()));
class ResourcesFleetScreen extends StatefulWidget {
const ResourcesFleetScreen({super.key});
@ -570,13 +569,16 @@ class _ResourcesFleetScreenState extends State<ResourcesFleetScreen> {
itemBuilder: (context, idx) {
final it = filtered[idx];
return GestureDetector(
onTap: (){
Navigator.push(
onTap: () async{
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TankerDetailsPage(tankerDetails: it),
),
);
if (result == true) {
_fetchTankers();
}
},
child: TankCard(
title: it.tanker_name,
@ -735,21 +737,7 @@ class TankCard extends StatelessWidget {
const SizedBox(height: 10),
Row(
children: [
ClipOval(
child: Container(
height: 12,
width: 12,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
image: (AppSettings.profilePictureUrl != '' && AppSettings.profilePictureUrl != 'null')
? NetworkImage(AppSettings.profilePictureUrl)
: const AssetImage("images/profile_pic.png") as ImageProvider,
fit: BoxFit.cover,
),
),
),
),
Image.asset('images/avatar.png', width: 12, height: 12),
const SizedBox(width: 6),
Expanded(
child: Text(owner, style: fontTextStyle(8, const Color(0xFF646566), FontWeight.w400)),

@ -1,4 +1,36 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../common/settings.dart';
class FirstCharUppercaseFormatter extends TextInputFormatter {
const FirstCharUppercaseFormatter();
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue,
) {
final text = newValue.text;
if (text.isEmpty) return newValue;
// Find first non-space char
final i = text.indexOf(RegExp(r'\S'));
if (i == -1) return newValue;
final first = text[i];
final upper = first.toUpperCase();
if (first == upper) return newValue;
final newText = text.replaceRange(i, i + 1, upper);
return newValue.copyWith(
text: newText,
selection: newValue.selection,
composing: TextRange.empty,
);
}
}
class TankerDetailsPage extends StatefulWidget {
var tankerDetails;
@ -9,50 +41,440 @@ class TankerDetailsPage extends StatefulWidget {
}
class _TankerDetailsPageState extends State<TankerDetailsPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios, color: Colors.black),
onPressed: () => Navigator.pop(context),
final _formKey = GlobalKey<FormState>();
final _nameCtrl = TextEditingController();
final _capacityCtrl = TextEditingController();
final _plateCtrl = TextEditingController();
final _mfgYearCtrl = TextEditingController();
final _insExpiryCtrl = TextEditingController();
// Dropdown selections (sheet)
String? selectedType;
String? selectedTypeOfWater;
// Dropdown options (adjust to your backend)
final List<String> tankerTypes = const [
"Standard Tanker",
"High-Capacity Tanker",
"Small Tanker",
];
final List<String> typeOfWater = const [
"Drinking water",
"Bore water",
];
String? _required(String? v, {String field = "This field"}) {
if (v == null || v.trim().isEmpty) return "$field is required";
return null;
}
int selectedTab = 0;
String search = '';
bool isLoading = false;
Color _chipColor(String s) {
switch (s) {
case 'filled':
return const Color(0xFFFFFFFF);
case 'available':
return const Color(0xFFE8F0FF);
case 'empty':
return const Color(0xFFFFEEEE);
case 'in-use':
return const Color(0xFFFFF0E6);
case 'maintenance':
return const Color(0xFFFFF4E6);
default:
return const Color(0xFFECECEC);
}
}
Color _chipTextColor(String s) {
switch (s) {
case 'filled':
return const Color(0xFF1D7AFC);
case 'available':
return const Color(0xFF0A9E04);
case 'empty':
return const Color(0xFFE2483D);
case 'in-use':
return const Color(0xFFEA843B);
case 'maintenance':
return const Color(0xFFD0AE3C);
default:
return Colors.black87;
}
}
Future<void> _pickInsuranceDate() async {
final picked = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime(2100),
builder: (context, child) {
return Theme(
data: Theme.of(context).copyWith(
dialogBackgroundColor: Colors.white,
colorScheme: Theme.of(context).colorScheme.copyWith(
primary: const Color(0xFF8270DB),
surface: Colors.white,
onSurface: const Color(0xFF101214),
),
),
child: child!,
);
},
);
if (picked != null) {
final dd = picked.day.toString().padLeft(2, '0');
final mm = picked.month.toString().padLeft(2, '0');
final yyyy = picked.year.toString();
_insExpiryCtrl.text = "$dd-$mm-$yyyy";
setState(() {});
}
}
void _resetForm() {
_formKey.currentState?.reset();
_nameCtrl.clear();
_capacityCtrl.clear();
_plateCtrl.clear();
_mfgYearCtrl.clear();
_insExpiryCtrl.clear();
selectedType = null;
selectedTypeOfWater = null;
}
String? fitToOption(String? incoming, List<String> options) {
if (incoming == null) return null;
final inc = incoming.trim();
if (inc.isEmpty) return null;
final match = options.firstWhere(
(o) => o.toLowerCase() == inc.toLowerCase(),
orElse: () => '',
);
return match.isEmpty ? null : match; // return the exact option string
}
Future<void> _updateTanker() async {
// Validate
final ok = _formKey.currentState?.validate() ?? false;
if (!ok) {
setState(() {}); // force rebuild to show errors
return;
}
// Build payload keys to match your backend
final payload = <String, dynamic>{
"tankerName": _nameCtrl.text.trim(),
"capacity": _capacityCtrl.text.trim(),
"typeofwater": selectedTypeOfWater ?? "",
"supplier_address": AppSettings.userAddress,
"supplier_name": AppSettings.userName,
"phoneNumber": AppSettings.phoneNumber,
"tanker_type": selectedType ?? "",
"license_plate": _plateCtrl.text.trim(),
"manufacturing_year": _mfgYearCtrl.text.trim(),
"insurance_exp_date": _insExpiryCtrl.text.trim(),
"delivery_fee": widget.tankerDetails.delivery_fee,
"pumping_fee":widget.tankerDetails.pumping_fee,
"price": widget.tankerDetails.price,
};
try {
final bool tankStatus = await AppSettings.updateTanker(payload,widget.tankerDetails.tanker_name);
if (!mounted) return;
if (tankStatus) {
AppSettings.longSuccessToast("Tanker Updated Successfully");
Navigator.pop(context, true); // close sheet
_resetForm();
// refresh from server
} else {
AppSettings.longFailedToast("Tanker update failed");
}
} catch (e) {
debugPrint("⚠️ update tanker error: $e");
if (!mounted) return;
AppSettings.longFailedToast("Something went wrong");
}
}
Future<void> openTankerSimpleSheet(BuildContext context) async {
_nameCtrl.text = widget.tankerDetails.tanker_name ?? '';
_capacityCtrl.text = widget.tankerDetails.capacity ?? '';
_plateCtrl.text = widget.tankerDetails.license_plate ?? '';
_mfgYearCtrl.text = widget.tankerDetails.manufacturing_year ?? '';
_insExpiryCtrl.text = widget.tankerDetails.insurance_expiry ?? '';
selectedType = fitToOption(widget.tankerDetails.tanker_type, tankerTypes);
selectedTypeOfWater = fitToOption(widget.tankerDetails.type_of_water, typeOfWater);
await showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) {
final viewInsets = MediaQuery.of(context).viewInsets.bottom;
return FractionallySizedBox(
heightFactor: 0.75,
child: Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
title: Text(
widget.tankerDetails.tanker_name.isNotEmpty
? widget.tankerDetails.tanker_name[0].toUpperCase() +
widget.tankerDetails.tanker_name.substring(1)
: '',
style: const TextStyle(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.w600,
child: Padding(
padding: EdgeInsets.fromLTRB(20, 16, 20, 20 + viewInsets),
child: Form(
key: _formKey,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
Expanded(
child: Center(
child: Container(
width: 86,
height: 4,
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
color: const Color(0xFFE0E0E0),
borderRadius: BorderRadius.circular(2),
),
),
centerTitle: false,
actions: [
TextButton(
onPressed: () {},
child: const Text(
"HELP",
style: TextStyle(
color: Color(0xFF4F46E5),
fontWeight: FontWeight.w600,
fontSize: 14,
),
),
],
),
_LabeledField(
label: "Tanker Name *",
child: TextFormField(
controller: _nameCtrl,
validator: (v) => _required(v, field: "Tanker Name"),
textCapitalization: TextCapitalization.none,
inputFormatters: const [
FirstCharUppercaseFormatter(), // << live first-letter caps
],
decoration: InputDecoration(
hintText: "Enter Tanker Name",
hintStyle: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400),
border: const OutlineInputBorder(),
isDense: true,
),
textInputAction: TextInputAction.next,
),
),
_LabeledField(
label: "Tanker Capacity (in L) *",
child: TextFormField(
controller: _capacityCtrl,
validator: (v) => _required(v, field: "Tanker Capacity"),
decoration: InputDecoration(
hintText: "10,000",
hintStyle: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400),
border: const OutlineInputBorder(),
isDense: true,
),
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[0-9,]')),
],
textInputAction: TextInputAction.next,
),
),
_LabeledField(
label: "Tanker Type *",
child: DropdownButtonFormField<String>(
value: selectedType,
items: tankerTypes
.map((t) => DropdownMenuItem(value: t, child: Text(t)))
.toList(),
onChanged: (v) => setState(() => selectedType = v),
validator: (v) => v == null || v.isEmpty ? "Tanker Type is required" : null,
isExpanded: true,
alignment: Alignment.centerLeft,
hint: Text(
"Select Type",
style: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400),
),
icon: Image.asset('images/downarrow.png', width: 16, height: 16),
decoration: const InputDecoration(
border: OutlineInputBorder(),
isDense: false,
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 14),
),
),
),
_LabeledField(
label: "Type of water *",
child: DropdownButtonFormField<String>(
value: (selectedTypeOfWater != null && typeOfWater.contains(selectedTypeOfWater))
? selectedTypeOfWater
: null, // <- ensure null instead of "" or a non-member
items: typeOfWater
.map((t) => DropdownMenuItem(value: t, child: Text(t)))
.toList(),
onChanged: (v) => setState(() => selectedTypeOfWater = v),
validator: (v) => v == null || v.isEmpty ? "Type of water is required" : null,
isExpanded: true,
alignment: Alignment.centerLeft,
hint: Text(
"Select type of water",
style: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400),
),
icon: Image.asset('images/downarrow.png', width: 16, height: 16),
decoration: const InputDecoration(
border: OutlineInputBorder(),
isDense: false,
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 14),
),
),
),
_LabeledField(
label: "License Plate *",
child: TextFormField(
controller: _plateCtrl,
validator: (v) => _required(v, field: "License Plate"),
decoration: InputDecoration(
hintText: "AB 05 H 4948",
hintStyle: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400),
border: const OutlineInputBorder(),
isDense: true,
),
textCapitalization: TextCapitalization.characters,
textInputAction: TextInputAction.next,
),
),
_LabeledField(
label: "Manufacturing Year (opt)",
child: TextFormField(
controller: _mfgYearCtrl,
decoration: InputDecoration(
hintText: "YYYY",
hintStyle: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400),
border: const OutlineInputBorder(),
isDense: true,
),
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(4),
],
textInputAction: TextInputAction.next,
),
),
_LabeledField(
label: "Insurance Expiry Date (opt)",
child: TextFormField(
controller: _insExpiryCtrl,
readOnly: true,
decoration: InputDecoration(
hintText: "DD-MM-YYYY",
hintStyle: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400),
border: const OutlineInputBorder(),
isDense: true,
suffixIcon: const Icon(Icons.calendar_today_outlined, size: 18),
),
onTap: _pickInsuranceDate,
),
),
const SizedBox(height: 20),
SizedBox(
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF8270DB),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
),
onPressed: (){
_updateTanker();
/*if (_formKey.currentState!.validate()) {
final updatedTanker = {
"tanker_name": _nameCtrl.text.trim(),
"capacity": _capacityCtrl.text.trim(),
"license_plate": _plateCtrl.text.trim(),
"manufacturing_year": _mfgYearCtrl.text.trim(),
"insurance_expiry": _insExpiryCtrl.text.trim(),
"tanker_type": selectedType,
"type_of_water": selectedTypeOfWater,
};
// You can now call your update API or local DB function
print("Updated Tanker: $updatedTanker");
Navigator.pop(context);
}*/
},
child: Text(
"Update",
style: fontTextStyle(14, Colors.white, FontWeight.w600),
),
),
),
],
),
),
),
),
),
);
},
);
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
// Return true to indicate successful tanker update refresh
Navigator.pop(context, true);
return false; // prevent default pop since we manually handled it
},
child: Scaffold(
backgroundColor: Color(0XFFF1F1F1),
appBar: AppSettings.supplierAppBarWithActionsText(widget.tankerDetails.tanker_name.isNotEmpty
? widget.tankerDetails.tanker_name[0].toUpperCase() +
widget.tankerDetails.tanker_name.substring(1)
: '', context),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.all(0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 🛻 Tanker Image
Container(
decoration: const BoxDecoration(
color: Color(0XFFFFFFFF),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24),
),
),
padding: const EdgeInsets.all(16),
child: Column(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Stack(
@ -79,75 +501,207 @@ class _TankerDetailsPageState extends State<TankerDetailsPage> {
),
const SizedBox(height: 16),
Row(
children: [
_buildStatusChip("filled", const Color(0xFF4F46E5)),
const SizedBox(width: 8),
_buildStatusChip("available", const Color(0xFF0A9E04)),
],
children: widget.tankerDetails.availability
.map<Widget>((s) {
final chipTextColor = _chipTextColor(s);
return Container(
margin: const EdgeInsets.only(right: 6),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: _chipColor(s),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: chipTextColor, width: 1),
),
child: Text(
s,
style: fontTextStyle(10, chipTextColor, FontWeight.w400),
),
);
})
.toList(),
),
const SizedBox(height: 16),
// 🚚 Tanker Info
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const CircleAvatar(
radius: 18,
backgroundImage: AssetImage('images/avatar.png'),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Ramesh Krishna",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.black,
widget.tankerDetails.tanker_name,
style: fontTextStyle(16, const Color(0xFF2A2A2A), FontWeight.w600),
),
SizedBox(height: 2),
Text(
widget.tankerDetails.type_of_water+' - '+widget.tankerDetails.capacity+' L',
style: fontTextStyle(10, const Color(0xFF646566), FontWeight.w400),
),
SizedBox(height: 2),
Text(
widget.tankerDetails.tanker_name,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: Colors.black,
widget.tankerDetails.license_plate,
style: fontTextStyle(10, const Color(0xFF646566), FontWeight.w400),
),
],
),
SizedBox(height: 2),
),
PopupMenuButton<String>(
// 🔁 Use `child:` so you can place any widget (your 3-dots image)
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0),
child: Image.asset(
'images/popup_menu.png', // your 3-dots image
width: 22,
height: 22,
// If you want to tint it like an icon:
color: Color(0XFF939495), // remove if you want original colors
colorBlendMode: BlendMode.srcIn,
),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
offset: const Offset(0, 40),
color: Colors.white,
elevation: 4,
onSelected: (value) {
if (value == 'edit') {
openTankerSimpleSheet(context);
} else if (value == 'disable') {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Disable selected')),
);
} else if (value == 'delete') {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Delete selected')),
);
}
},
itemBuilder: (context) => [
PopupMenuItem(
value: 'edit',
child: Row(
children: [
Image.asset(
'images/edit.png',
width: 20,
height: 20,
color: Color(0XFF646566), // tint (optional)
colorBlendMode: BlendMode.srcIn,
),
const SizedBox(width: 12),
Text(
widget.tankerDetails.type_of_water+' - '+widget.tankerDetails.capacity+' L',
style: TextStyle(
fontSize: 12,
color: Colors.black54,
'Edit',
style: fontTextStyle(14, const Color(0XFF646566), FontWeight.w400),
),
],
),
),
PopupMenuItem(
value: 'disable',
child: Row(
children: [
Image.asset(
'images/disable.png',
width: 20,
height: 20,
color: Color(0XFF646566), // tint (optional)
colorBlendMode: BlendMode.srcIn,
),
const SizedBox(width: 12),
Text(
'Disable',
style: fontTextStyle(14, const Color(0XFF646566), FontWeight.w400),
),
],
),
),
PopupMenuItem(
value: 'delete',
child: Row(
children: [
Image.asset(
'images/delete.png',
width: 20,
height: 20,
color: Color(0XFFE2483D), // red like your example
colorBlendMode: BlendMode.srcIn,
),
const SizedBox(width: 12),
Text(
widget.tankerDetails.license_plate,
style: TextStyle(
fontSize: 12,
color: Colors.black54,
fontWeight: FontWeight.w500,
'Delete',
style: fontTextStyle(14, const Color(0xFFE2483D), FontWeight.w400),
),
],
),
),
],
)
],
),
const SizedBox(height: 24),
const SizedBox(height: 12),
// 📍 Recent Trips
const Text(
"RECENT TRIPS",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Colors.black87,
Row(
children: [
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFFFFFFF),
foregroundColor: const Color(0xFF515253),
padding: const EdgeInsets.symmetric(vertical: 10),
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
side: const BorderSide(color: Color(0xFF939495), width: 0.5),
),
// Or: side: const BorderSide(color: Color(0xFFC3C4C4), width: 1.2),
),
onPressed: () async {
// _showAssignTankerBottomSheet();
},
child: Text(
"Change Status",
style: fontTextStyle(14, const Color(0xFF515253), FontWeight.w500),
),
)
),
SizedBox(width: 8),
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF8270DB),
foregroundColor: const Color(0xFFFFFFFF),
padding: const EdgeInsets.symmetric(vertical: 10),
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
side: const BorderSide(color: Color(0xFF8270DB), width: 0.5),
),
// Or: side: const BorderSide(color: Color(0xFFC3C4C4), width: 1.2),
),
onPressed: () async {
// _showAssignTankerBottomSheet();
},
child: Text(
"Assign",
style: fontTextStyle(14, const Color(0xFFFFFFFF), FontWeight.w500),
),
)
),
],
),
],
),
),
Padding(padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"RECENT TRIPS",
style: fontTextStyle(10, const Color(0xFF343637), FontWeight.w600),
),
const SizedBox(height: 12),
@ -164,11 +718,15 @@ class _TankerDetailsPageState extends State<TankerDetailsPage> {
from: "Bachupally Filling Station",
to: "Akriti Heights",
),
const SizedBox(height: 30),
],
),
),
],
),
),
),
)
);
}
@ -278,4 +836,35 @@ class _TankerDetailsPageState extends State<TankerDetailsPage> {
}
// ====== Labeled Field Wrapper ======
class _LabeledField extends StatelessWidget {
final String label;
final Widget child;
const _LabeledField({required this.label, required this.child});
String _capFirstWord(String input) {
if (input.isEmpty) return input;
final i = input.indexOf(RegExp(r'\S'));
if (i == -1) return input;
return input.replaceRange(i, i + 1, input[i].toUpperCase());
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 14.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_capFirstWord(label),
style: fontTextStyle(12, const Color(0xFF515253), FontWeight.w600),
),
const SizedBox(height: 6),
child,
],
),
);
}
}

@ -9,6 +9,10 @@ class TankersModel {
String status='';
String license_plate='';
String supplier_name='';
String manufacturing_year='';
String insurance_expiry='';
String tanker_type='';
String pumping_fee='';
List<dynamic> availability= [];
TankersModel();
@ -25,7 +29,10 @@ class TankersModel {
rtvm.price = json['price'] ?? '';
rtvm.delivery_fee = json['delivery_fee'] ?? '';
rtvm.availability = json['availability'] ?? [];
rtvm.manufacturing_year = json['manufacturing_year'] ?? '';
rtvm.insurance_expiry = json['insurance_exp_date'] ?? '';
rtvm.tanker_type = json['tanker_type'] ?? '';
rtvm.pumping_fee = json['pumping_fee'] ?? '';
return rtvm;
}
Map<String, dynamic> toJson() => {

Loading…
Cancel
Save