driver details screen hanges

master
Sneha 1 month ago
parent c9ed7a8cfb
commit 5965fc5199

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

@ -160,9 +160,13 @@ class AppSettings{
static String getTankersUrl = host + 'getTankers';
static String getTankerDetailsByNameUrl = host + 'getsingledetails';
static String addTankerUrl = host + 'addTankers';
static String updateTankerAvailabilityUrl = host + 'updatetankerstatus';
static String deleteTankerUrl = host + 'deleteTanker';
static String updateTankerUrl = host + 'updateTankers';
static String getDriversUrl = host + 'getalldeliveryboys';
static String addDriversUrl = host + 'addDeliveryboys';
static String updateDriversUrl = host + 'updatedeliveryboy';
static String deleteDriverUrl = host + 'deletedeliveryboy';
static String addSourceLocationsUrl = host + 'addSource';
static String getSourceLoctaionsUrl = host + 'getallsourcesofsupplier';
static String setRatesDailyUrl = host + 'tankers';
@ -558,7 +562,10 @@ class AppSettings{
}
static Future<bool> addTankers(payload) async {
var response = await http.post(Uri.parse(addTankerUrl + '/' + supplierId),
var uri = Uri.parse(addTankerUrl + '/' + supplierId);
var response = await http.post(uri,
body: json.encode(payload), headers: await buildRequestHeaders());
if (response.statusCode == 200) {
@ -573,7 +580,7 @@ class AppSettings{
} else if (response.statusCode == 401) {
bool status = await AppSettings.resetToken();
if (status) {
response = await http.post(Uri.parse(addTankerUrl + '/' + supplierId),
response = await http.post(uri,
body: json.encode(payload), headers: await buildRequestHeaders());
if (response.statusCode == 200) {
return true;
@ -622,6 +629,71 @@ class AppSettings{
}
}
static Future<bool> updateTankerAvailability(payload) async {
var uri = Uri.parse(updateTankerAvailabilityUrl + '/' + supplierId);
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<bool> deleteTanker(tankerName) async {
var uri = Uri.parse(deleteTankerUrl + '/' + supplierId);
uri = uri.replace(query: 'tankerName=$tankerName');
var response = await http.put(uri, headers: await buildPutRequestHeaders());
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, headers: await buildPutRequestHeaders());
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');
@ -677,6 +749,71 @@ class AppSettings{
}
}
static Future<bool> updateDrivers(payload,phone) async {
var uri =Uri.parse(updateDriversUrl + '/' + supplierId);
uri = uri.replace(query: 'phone=$phone');
var response = await http.post(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.post(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<bool> deleteDriver(driverPhone) async {
var uri = Uri.parse(deleteDriverUrl + '/' + supplierId);
uri = uri.replace(query: 'phone=$driverPhone');
var response = await http.put(uri, headers: await buildPutRequestHeaders());
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, headers: await buildPutRequestHeaders());
if (response.statusCode == 200) {
return true;
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
}
static Future<String> getSourceLoctaions() async {
var uri = Uri.parse(getSourceLoctaionsUrl+'/'+supplierId);
//uri = uri.replace(query: 'supplierId=$supplierId');
@ -1033,6 +1170,7 @@ class AppSettings{
backgroundColor: Colors.white,
elevation: 0,
scrolledUnderElevation: 0,
titleSpacing: 0,
title: Text(
title,
style: fontTextStyle(16, Color(0XFF2A2A2A), FontWeight.w600),
@ -1081,6 +1219,7 @@ class AppSettings{
backgroundColor: Colors.white,
elevation: 0,
scrolledUnderElevation: 0,
titleSpacing: 0,
title: Text(
title,
style: fontTextStyle(16, Color(0XFF2A2A2A), FontWeight.w600),

@ -614,6 +614,9 @@ class _AssignDriverScreenState extends State<AssignDriverScreen> {
final selectedDriver = selectedDriverIndex != null
? driversList[selectedDriverIndex!]
: null;
final selectedSource = selectedSourceIndex != null
? sourceLocationsList[selectedSourceIndex!]
: null;
AppSettings.preLoaderDialog(context);
bool isOnline =
@ -621,12 +624,10 @@ class _AssignDriverScreenState extends State<AssignDriverScreen> {
if (isOnline) {
var payload = <String, dynamic>{};
payload["tankerName"] =
selectedTanker.tanker_name;
payload["delivery_agent"] =
selectedDriver?.driver_name;
payload["delivery_agent_mobile"] =
selectedDriver?.phone_number;
payload["tankerName"] = selectedTanker.tanker_name;
payload["delivery_agent"] = selectedDriver?.driver_name;
payload["delivery_agent_mobile"] = selectedDriver?.phone_number;
payload["water_source_location"] = selectedSource?.source_name;
bool status = await AppSettings.assignTanker(
payload, widget.order.dbId);

@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:supplier_new/common/settings.dart';
import '../resources/drivers_model.dart';
import '../resources/source_loctaions_model.dart';
import '../resources/tankers_model.dart';
class ChangeDriverScreen extends StatefulWidget {
@ -19,10 +20,13 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
double totalFare = 0.0;
bool isLoading = false;
bool isTankersDataLoading = false;
bool isSourceDataLoading = false;
List<DriversModel> driversList = [];
List<TankersModel> tankersList = [];
List<SourceLocationsModel> sourcesList = [];
TankersModel? orderTanker;
DriversModel? orderDriver;
SourceLocationsModel? orderSource;
@override
@ -31,6 +35,7 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
super.initState();
_fetchTankers();
_fetchDrivers();
_fetchSources();
advance = 150;
advancePayable = advance;
totalFare = advance + double.parse(widget.order.quoted_amount);
@ -99,6 +104,37 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
}
}
Future<void> _fetchSources() async {
setState(() => isSourceDataLoading = true);
try {
final response = await AppSettings.getSourceLoctaions();
final data = (jsonDecode(response)['data'] as List)
.map((e) => SourceLocationsModel.fromJson(e))
.toList();
if (!mounted) return;
final wanted =
(widget.order.water_source_location ?? '').trim().toLowerCase();
SourceLocationsModel? matched;
for (final s in data) {
if ((s.source_name ?? '').trim().toLowerCase() == wanted) {
matched = s;
break;
}
}
setState(() {
sourcesList = data;
orderSource = matched;
isSourceDataLoading = false;
});
} catch (e) {
debugPrint("⚠️ Error fetching source locations: $e");
setState(() => isSourceDataLoading = false);
}
}
Widget _assignedTankerDetails() {
if (isTankersDataLoading) {
return const Center(child: CircularProgressIndicator());
@ -329,10 +365,74 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
);
}
Widget _assignedSourceDetails() {
if (isSourceDataLoading) {
return const Center(child: CircularProgressIndicator());
}
if (orderSource == null) {
// fallback if not found
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: Row(
children: [
Image.asset('images/avatar.png', width: 24, height: 24),
const SizedBox(width: 8),
Expanded(
child: Text(
widget.order.water_source_location ?? "Driver",
style: fontTextStyle(14, const Color(0XFF2D2E30), FontWeight.w500),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
);
}
final d = orderSource!;
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.source_name ?? "",
style: fontTextStyle(14, const Color(0xFF2D2E30), FontWeight.w500),
maxLines: 1, overflow: TextOverflow.ellipsis,
),
),
],
),
/* const SizedBox(height: 8),
_line("Phone", d.phone_number ?? ""),*/
],
),
);
}
void _showAssignTankerBottomSheet() {
// 🔸 Find default selected indexes
int? selectedTankerIndex;
int? selectedDriverIndex;
int? selectedSourceIndex;
int _capToLiters(dynamic cap) {
if (cap == null) return -1;
@ -360,6 +460,11 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
selectedDriverIndex = driversList.indexWhere(
(d) => d.driver_name == widget.order.delivery_agent_name);
selectedSourceIndex = sourcesList.indexWhere(
(d) => d.source_name == widget.order.water_source_location);
showModalBottomSheet(
backgroundColor: const Color(0XFFFFFFFF),
context: context,
@ -599,6 +704,105 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
);
},
),
const SizedBox(height: 16),
/// 🧍 Source List
Text(
"SELECTED SOURCE LOCATION",
style: fontTextStyle(
10, const Color(0XFF2D2E30), FontWeight.w600),
),
const SizedBox(height: 12),
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(
shrinkWrap: true,
physics:
const NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: sourcesList.length,
separatorBuilder: (_, __) =>
const SizedBox(height: 12),
itemBuilder: (context, idx) {
final d = sourcesList[idx];
final isSelected =
selectedSourceIndex == idx;
/* final statusColor = d.status == "available"
? const Color(0XFF0A9E04)
: (d.status == "on delivery"
? const Color(0XFFD0AE3C)
: (d.status == "offline"
? const Color(0XFF939495)
: Colors.grey));*/
return GestureDetector(
onTap: () {
setModalState(() {
selectedSourceIndex = idx;
});
},
child: Card(
color: const Color(0XFFFFFFFF),
elevation: 1,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(12),
side: BorderSide(
color: isSelected
? primaryColor
: const Color(0XFFC3C4C4),
width: 1,
),
),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Row(
children: [
Image.asset(
'images/avatar.png',
fit: BoxFit.cover,
width: 20,
height: 20,
),
const SizedBox(width: 8),
Expanded(
child: Text(
d.source_name,
style: fontTextStyle(
14,
const Color(
0XFF2D2E30),
FontWeight.w500),
),
),
],
),
),
),
);
},
),
],
),
),
@ -625,6 +829,9 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
final selectedDriver = selectedDriverIndex != null
? driversList[selectedDriverIndex!]
: null;
final selectedSource = selectedSourceIndex != null
? sourcesList[selectedSourceIndex!]
: null;
AppSettings.preLoaderDialog(context);
bool isOnline =
@ -636,8 +843,8 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
selectedTanker.tanker_name;
payload["delivery_agent"] =
selectedDriver?.driver_name;
payload["delivery_agent_mobile"] =
selectedDriver?.phone_number;
payload["delivery_agent_mobile"] = selectedDriver?.phone_number;
payload["water_source_location"] = selectedSource?.source_name;
bool status = await AppSettings.assignTanker(
payload, widget.order.dbId);
@ -928,6 +1135,7 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
visible:widget.order.delivery_agent_name != null &&
widget.order.delivery_agent_name.toString().trim().isNotEmpty,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: MediaQuery.of(context).size.height * .012,
@ -942,7 +1150,28 @@ class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
),
_assignedDriverDetails()
],
))
)),
Visibility(
visible:widget.order.water_source_location != null &&
widget.order.water_source_location.toString().trim().isNotEmpty,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: MediaQuery.of(context).size.height * .012,
),
Text(
"ASSIGNED SOURCE LOCATION",
style: fontTextStyle(
10, const Color(0XFF646566), FontWeight.w600),
),
SizedBox(
height: MediaQuery.of(context).size.height * .012,
),
_assignedSourceDetails()
],
))
],
)),
],

@ -23,6 +23,7 @@ class OrdersModel {
String imageAsset='images/building.png';
String delivery_agent_name = '';
String tanker_name = '';
String water_source_location='';
OrdersModel();
@ -43,6 +44,7 @@ class OrdersModel {
rtvm.lat=json['latitude'] ?? 0.0;
rtvm.delivery_agent_name = json['delivery_agent'] ?? '';
rtvm.tanker_name = json['tankerName'] ?? '';
rtvm.water_source_location = json['water_source_location'] ?? '';
// Split and trim
List<String> parts = rtvm.address.split(',').map((e) => e.trim()).toList();

@ -57,7 +57,6 @@ class _SourceLocationState extends State<SourceLocation> {
bool isLoading = false;
int currentStep = 2;
List<SourceLocationsModel> sourceLocationsList = [];
final _locationNameController = TextEditingController();
bool addBusinessAsSource = false;
final List<String> waterTypes = [
@ -170,7 +169,7 @@ class _SourceLocationState extends State<SourceLocation> {
_LabeledField(
label: "Location Name *",
child: TextFormField(
controller: _locationNameController,
controller: _nameCtrl,
validator: (v) => _required(v, field: "Location Name"),
textCapitalization: TextCapitalization.none,
inputFormatters: const [

File diff suppressed because it is too large Load Diff

@ -5,8 +5,11 @@ class DriversModel {
String address='';
String deliveries='13';
String commision='';
String years_of_experience='';
List<String> availability= ['filled', 'available'];
String phone_number='';
String alt_phone_number='';
String license_number='';
DriversModel();
factory DriversModel.fromJson(Map<String, dynamic> json){
@ -17,7 +20,10 @@ class DriversModel {
rtvm.driver_name = json['name'] ?? '';
rtvm.status = json['status'] ?? '';
rtvm.address = json['address'] ?? '';
rtvm.phone_number = json['phone'] ?? '';
rtvm.phone_number =json['phone'] ?? '';
rtvm.alt_phone_number =json['alternativeContactNumber'] ?? '';
rtvm.years_of_experience = json['years_of_experience'] ?? '';
rtvm.license_number = json['license_number'] ?? '';
return rtvm;
}
}

@ -10,9 +10,9 @@ class FirstCharUppercaseFormatter extends TextInputFormatter {
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue,
) {
TextEditingValue oldValue,
TextEditingValue newValue,
) {
final text = newValue.text;
if (text.isEmpty) return newValue;
@ -63,7 +63,11 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
// Dropdown state
String? _status; // 'available' | 'on delivery' | 'offline'
final List<String> _statusOptions = const ['available', 'on delivery', 'offline'];
final List<String> _statusOptions = const [
'available',
'on delivery',
'offline'
];
String? selectedLicense;
final List<String> licenseNumbers = const [
@ -73,7 +77,8 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
];
String? selectedExperience; // years as string
final List<String> yearOptions = List<String>.generate(41, (i) => '$i'); // 0..40
final List<String> yearOptions =
List<String>.generate(41, (i) => '$i'); // 0..40
String? _required(String? v, {String field = "This field"}) {
if (v == null || v.trim().isEmpty) return "$field is required";
@ -147,7 +152,9 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
final payload = <String, dynamic>{
"Name": _nameCtrl.text.trim(),
"license_number": selectedLicense ?? "",
"address": _locationCtrl.text.trim().isEmpty ? AppSettings.userAddress : _locationCtrl.text.trim(),
"address": _locationCtrl.text.trim().isEmpty
? AppSettings.userAddress
: _locationCtrl.text.trim(),
"supplier_name": AppSettings.userName,
"phone": _mobileCtrl.text.trim(),
"alternativeContactNumber": _altMobileCtrl.text.trim(),
@ -175,17 +182,16 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
}
// ---------- bottom sheet ----------
Future<void> _openDriverFormSheet(BuildContext context)async {
Future<void> _openDriverFormSheet(BuildContext context) async {
await showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent, // style inner container
backgroundColor: Colors.transparent, // style inner container
builder: (context) {
final bottomInset = MediaQuery.of(context).viewInsets.bottom;
return FractionallySizedBox(
heightFactor: 0.75, // fixed height (75% of screen)
heightFactor: 0.75, // fixed height (75% of screen)
child: Container(
decoration: const BoxDecoration(
color: Colors.white,
@ -229,7 +235,8 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
],
decoration: InputDecoration(
hintText: "Full Name",
hintStyle: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400),
hintStyle: fontTextStyle(
14, const Color(0xFF939495), FontWeight.w400),
border: const OutlineInputBorder(),
isDense: true,
),
@ -242,21 +249,27 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
child: DropdownButtonFormField<String>(
value: selectedLicense,
items: licenseNumbers
.map((t) => DropdownMenuItem(value: t, child: Text(t)))
.map((t) =>
DropdownMenuItem(value: t, child: Text(t)))
.toList(),
onChanged: (v) => setState(() => selectedLicense = v),
validator: (v) => v == null || v.isEmpty ? "Driver License required" : null,
validator: (v) => v == null || v.isEmpty
? "Driver License required"
: null,
isExpanded: true,
alignment: Alignment.centerLeft,
hint: Text(
"Select License Number",
style: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400),
style: fontTextStyle(
14, const Color(0xFF939495), FontWeight.w400),
),
icon: Image.asset('images/downarrow.png', width: 16, height: 16),
icon: Image.asset('images/downarrow.png',
width: 16, height: 16),
decoration: const InputDecoration(
border: OutlineInputBorder(),
isDense: false,
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 14),
contentPadding: EdgeInsets.symmetric(
horizontal: 12, vertical: 14),
),
),
),
@ -266,21 +279,28 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
child: DropdownButtonFormField<String>(
value: selectedExperience,
items: yearOptions
.map((t) => DropdownMenuItem(value: t, child: Text(t)))
.map((t) =>
DropdownMenuItem(value: t, child: Text(t)))
.toList(),
onChanged: (v) => setState(() => selectedExperience = v),
validator: (v) => v == null || v.isEmpty ? "Experience is required" : null,
onChanged: (v) =>
setState(() => selectedExperience = v),
validator: (v) => v == null || v.isEmpty
? "Experience is required"
: null,
isExpanded: true,
alignment: Alignment.centerLeft,
hint: Text(
"Years",
style: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400),
style: fontTextStyle(
14, const Color(0xFF939495), FontWeight.w400),
),
icon: Image.asset('images/downarrow.png', width: 16, height: 16),
icon: Image.asset('images/downarrow.png',
width: 16, height: 16),
decoration: const InputDecoration(
border: OutlineInputBorder(),
isDense: false,
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 14),
contentPadding: EdgeInsets.symmetric(
horizontal: 12, vertical: 14),
),
),
),
@ -289,7 +309,8 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
label: "Phone Number *",
child: TextFormField(
controller: _mobileCtrl,
validator: (v) => _validatePhone(v, label: "Phone Number"),
validator: (v) =>
_validatePhone(v, label: "Phone Number"),
keyboardType: TextInputType.phone,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
@ -297,7 +318,8 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
],
decoration: InputDecoration(
hintText: "Mobile Number",
hintStyle: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400),
hintStyle: fontTextStyle(
14, const Color(0xFF939495), FontWeight.w400),
border: const OutlineInputBorder(),
isDense: true,
),
@ -310,8 +332,10 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
child: TextFormField(
controller: _altMobileCtrl,
validator: (v) {
if (v == null || v.trim().isEmpty) return null; // optional
return _validatePhone(v, label: "Alternate Phone Number");
if (v == null || v.trim().isEmpty)
return null; // optional
return _validatePhone(v,
label: "Alternate Phone Number");
},
keyboardType: TextInputType.phone,
inputFormatters: [
@ -320,7 +344,8 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
],
decoration: InputDecoration(
hintText: "Mobile Number",
hintStyle: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400),
hintStyle: fontTextStyle(
14, const Color(0xFF939495), FontWeight.w400),
border: const OutlineInputBorder(),
isDense: true,
),
@ -339,7 +364,8 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
],
decoration: InputDecoration(
hintText: "Area / locality",
hintStyle: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400),
hintStyle: fontTextStyle(
14, const Color(0xFF939495), FontWeight.w400),
border: const OutlineInputBorder(),
isDense: true,
),
@ -352,21 +378,26 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
child: DropdownButtonFormField<String>(
value: _status,
items: _statusOptions
.map((s) => DropdownMenuItem(value: s, child: Text(s)))
.map((s) =>
DropdownMenuItem(value: s, child: Text(s)))
.toList(),
onChanged: (v) => setState(() => _status = v),
validator: (v) => v == null || v.isEmpty ? "Status is required" : null,
validator: (v) => v == null || v.isEmpty
? "Status is required"
: null,
isExpanded: true,
alignment: Alignment.centerLeft,
hint: Text(
"Select status",
style: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400),
style: fontTextStyle(
14, const Color(0xFF939495), FontWeight.w400),
),
icon: const Icon(Icons.keyboard_arrow_down_rounded),
decoration: const InputDecoration(
border: OutlineInputBorder(),
isDense: false,
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 14),
contentPadding: EdgeInsets.symmetric(
horizontal: 12, vertical: 14),
),
),
),
@ -387,7 +418,8 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
onPressed: _addDriver,
child: Text(
"Save",
style: fontTextStyle(14, Colors.white, FontWeight.w600),
style: fontTextStyle(
14, Colors.white, FontWeight.w600),
),
),
),
@ -405,6 +437,11 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
// ---------- UI ----------
@override
Widget build(BuildContext context) {
final filtered = driversList.where((it) {
final q = search.trim().toLowerCase();
if (q.isEmpty) return true;
return it.driver_name.toLowerCase().contains(q);
}).toList();
return Scaffold(
backgroundColor: Colors.white,
body: Column(
@ -432,20 +469,27 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Image.asset('images/drivers.png', fit: BoxFit.contain),
child: Image.asset('images/drivers.png',
fit: BoxFit.contain),
),
),
const SizedBox(height: 8),
Text('Total Drivers', style: fontTextStyle(12, const Color(0xFF2D2E30), FontWeight.w500)),
Text('Total Drivers',
style: fontTextStyle(
12, const Color(0xFF2D2E30), FontWeight.w500)),
],
),
const Spacer(),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(driversList.length.toString(), style: fontTextStyle(24, const Color(0xFF0D3771), FontWeight.w500)),
Text(driversList.length.toString(),
style: fontTextStyle(
24, const Color(0xFF0D3771), FontWeight.w500)),
const SizedBox(height: 6),
Text('+1 since last month', style: fontTextStyle(10, const Color(0xFF646566), FontWeight.w400)),
Text('+1 since last month',
style: fontTextStyle(
10, const Color(0xFF646566), FontWeight.w400)),
],
),
],
@ -458,9 +502,11 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
child: IntrinsicHeight(
child: Row(
children: const [
Expanded(child: SmallMetricBox(title: 'On delivery', value: '2')),
Expanded(
child: SmallMetricBox(title: 'On delivery', value: '2')),
SizedBox(width: 8),
Expanded(child: SmallMetricBox(title: 'Available', value: '3')),
Expanded(
child: SmallMetricBox(title: 'Available', value: '3')),
SizedBox(width: 8),
Expanded(child: SmallMetricBox(title: 'Offline', value: '1')),
],
@ -482,24 +528,31 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
children: [
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 6),
decoration: BoxDecoration(
border: Border.all(color: const Color(0xFF939495), width: 0.5),
border: Border.all(
color: const Color(0xFF939495), width: 0.5),
borderRadius: BorderRadius.circular(22),
),
child: Row(
children: [
Image.asset('images/search.png', width: 18, height: 18),
Image.asset('images/search.png',
width: 18, height: 18),
const SizedBox(width: 8),
Expanded(
child: TextField(
decoration: InputDecoration(
hintText: 'Search',
hintStyle: fontTextStyle(12, const Color(0xFF939495), FontWeight.w400),
hintStyle: fontTextStyle(
12,
const Color(0xFF939495),
FontWeight.w400),
border: InputBorder.none,
isDense: true,
),
onChanged: (v) => setState(() => search = v),
onChanged: (v) =>
setState(() => search = v),
),
),
],
@ -507,39 +560,62 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
),
),
const SizedBox(width: 16),
Image.asset("images/icon_tune.png", width: 24, height: 24),
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),
Expanded(
child: isLoading
? const Center(child: CircularProgressIndicator())
: ListView.separated(
itemCount: driversList.length,
separatorBuilder: (_, __) => const SizedBox(height: 12),
itemBuilder: (context, idx) {
final d = driversList[idx];
return GestureDetector(
onTap: (){
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DriverDetailsPage(driverDetails: d),
),
);
},
child: DriverCard(
name: d.driver_name,
status: d.status,
location: d.address,
deliveries: int.tryParse(d.deliveries) ?? 0,
commission: d.commision,
),
);
},
),
: (filtered.isEmpty
? Center(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 12),
child: Text(
'No Data Available',
style: fontTextStyle(
12,
const Color(0xFF939495),
FontWeight.w500),
),
),
)
: ListView.separated(
itemCount: filtered.length,
separatorBuilder: (_, __) =>
const SizedBox(height: 12),
itemBuilder: (context, idx) {
final d = filtered[idx];
return GestureDetector(
onTap: () async{
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
DriverDetailsPage(
driverDetails: d),
),
);
if (result == true) {
_fetchDrivers();
}
},
child: DriverCard(
name: d.driver_name,
status: d.status,
location: d.address,
deliveries:
int.tryParse(d.deliveries) ?? 0,
commission: d.commision,
),
);
},
)),
),
],
),
@ -580,9 +656,13 @@ class SmallMetricBox extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: fontTextStyle(12, const Color(0xFF2D2E30), FontWeight.w500)),
Text(title,
style:
fontTextStyle(12, const Color(0xFF2D2E30), FontWeight.w500)),
const SizedBox(height: 4),
Text(value, style: fontTextStyle(24, const Color(0xFF0D3771), FontWeight.w500)),
Text(value,
style:
fontTextStyle(24, const Color(0xFF0D3771), FontWeight.w500)),
],
),
);
@ -607,8 +687,12 @@ class DriverCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final statusColor =
status == "available" ? Colors.green : (status == "on delivery" ? Colors.orange : Colors.grey);
final statusColor = switch (status) {
'available' => const Color(0xFF0A9E04),
'on delivery' => const Color(0xFFD0AE3C),
'offline' => const Color(0xFF939495),
_ => Colors.grey,
};
return Container(
padding: const EdgeInsets.all(14),
@ -626,22 +710,26 @@ class DriverCard extends StatelessWidget {
children: [
Row(
children: [
ClipOval(
child: Image.asset("images/profile_pic.png", height: 36, width: 36, fit: BoxFit.cover),
),
Image.asset("images/avatar.png",
height: 36, width: 36),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(name, style: fontTextStyle(14, const Color(0xFF2D2E30), FontWeight.w500)),
Text(name,
style: fontTextStyle(
14, const Color(0xFF2D2E30), FontWeight.w500)),
const SizedBox(height: 4),
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),
),
child: Text(status, style: fontTextStyle(10, statusColor, FontWeight.w400)),
child: Text(status,
style: fontTextStyle(
10, statusColor, FontWeight.w400)),
),
],
),
@ -653,7 +741,8 @@ class DriverCard extends StatelessWidget {
color: Color(0xFFF5F6F6),
shape: BoxShape.circle,
),
child: Image.asset("images/phone_icon.png", width: 20, height: 20),
child:
Image.asset("images/phone_icon.png", width: 20, height: 20),
),
],
),
@ -664,8 +753,14 @@ class DriverCard extends StatelessWidget {
Text.rich(
TextSpan(
children: [
TextSpan(text: "Location\n", style: fontTextStyle(8, const Color(0xFF939495), FontWeight.w500)),
TextSpan(text: location, style: fontTextStyle(12, const Color(0xFF515253), FontWeight.w500)),
TextSpan(
text: "Location\n",
style: fontTextStyle(
8, const Color(0xFF939495), FontWeight.w500)),
TextSpan(
text: location,
style: fontTextStyle(
12, const Color(0xFF515253), FontWeight.w500)),
],
),
textAlign: TextAlign.center,
@ -673,21 +768,35 @@ class DriverCard extends StatelessWidget {
Text.rich(
TextSpan(
children: [
TextSpan(text: "Deliveries\n", style: fontTextStyle(8, const Color(0xFF939495), FontWeight.w400)),
TextSpan(text: deliveries.toString(), style: fontTextStyle(12, const Color(0xFF515253), FontWeight.w500)),
TextSpan(
text: "Deliveries\n",
style: fontTextStyle(
8, const Color(0xFF939495), FontWeight.w400)),
TextSpan(
text: deliveries.toString(),
style: fontTextStyle(
12, const Color(0xFF515253), FontWeight.w500)),
],
),
textAlign: TextAlign.center,
),
Text.rich(
Visibility(
visible:commission!='' ,
child: Text.rich(
TextSpan(
children: [
TextSpan(text: "Commission\n", style: fontTextStyle(8, const Color(0xFF939495), FontWeight.w400)),
TextSpan(text: commission, style: fontTextStyle(12, const Color(0xFF515253), FontWeight.w500)),
TextSpan(
text: "Commission\n",
style: fontTextStyle(
8, const Color(0xFF939495), FontWeight.w400)),
TextSpan(
text: commission,
style: fontTextStyle(
12, const Color(0xFF515253), FontWeight.w500)),
],
),
textAlign: TextAlign.center,
),
),)
],
),
const SizedBox(height: 12),
@ -697,19 +806,25 @@ class DriverCard extends StatelessWidget {
OutlinedButton(
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Color(0xFF939495)),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16)),
),
onPressed: () {},
child: Text("View Schedule", style: fontTextStyle(12, const Color(0xFF515253), FontWeight.w400)),
child: Text("View Schedule",
style: fontTextStyle(
12, const Color(0xFF515253), FontWeight.w400)),
),
const SizedBox(width: 12),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF8270DB),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16)),
),
onPressed: () {},
child: Text("Assign", style: fontTextStyle(12, const Color(0xFFFFFFFF), FontWeight.w400)),
child: Text("Assign",
style: fontTextStyle(
12, const Color(0xFFFFFFFF), FontWeight.w400)),
),
],
)
@ -729,6 +844,7 @@ class _LabeledField extends StatelessWidget {
if (i == -1) return input;
return input.replaceRange(i, i + 1, input[i].toUpperCase());
}
@override
Widget build(BuildContext context) {
return Padding(

@ -560,6 +560,20 @@ class _ResourcesFleetScreenState extends State<ResourcesFleetScreen> {
Expanded(
child: isLoading
? const Center(child: CircularProgressIndicator())
: (filtered.isEmpty
? Center(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 12),
child: Text(
'No Data Available',
style: fontTextStyle(
12,
const Color(0xFF939495),
FontWeight.w500),
),
),
)
: ListView.separated(
itemCount: filtered.length,
separatorBuilder: (_, __) => const SizedBox(height: 10),
@ -587,7 +601,7 @@ class _ResourcesFleetScreenState extends State<ResourcesFleetScreen> {
),
);
},
),
)),
),
],
),

@ -19,6 +19,11 @@ class _SourceDetailsScreenState extends State<SourceDetailsScreen> {
{"type": "Drinking Water", "liters": "10,000 L", "time": "7:02 PM", "date": "28 Jun 2025"},
];
double _avSize = 80; // avatar diameter
double _cardHPad = 16; // horizontal padding
double _cardTPad = 16; // top inner padding
double _avatarOverlap = 28; // how much avatar rises above the card
void _showMenu() {
showModalBottomSheet(
context: context,
@ -55,57 +60,201 @@ class _SourceDetailsScreenState extends State<SourceDetailsScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
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: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios_new, color: Colors.black),
onPressed: () => Navigator.pop(context),
),
title: Text( widget.sourceDetails.source_name.isNotEmpty
? widget.sourceDetails.source_name[0].toUpperCase() +
widget.sourceDetails.source_name.substring(1)
: '',
style: fontTextStyle(16, Colors.black, FontWeight.w600)),
actions: [
TextButton(
onPressed: () {},
child: Text("HELP",
style: fontTextStyle(12, const Color(0xFF8270DB), FontWeight.w600)),
),
],
),
appBar:AppSettings.supplierAppBarWithActionsText( widget.sourceDetails.source_name.isNotEmpty
? widget.sourceDetails.source_name[0].toUpperCase() +
widget.sourceDetails.source_name.substring(1)
: '', context),
body: SingleChildScrollView(
child: Column(
child:
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Image
SizedBox(height:MediaQuery.of(context).size.height * .1,),
// 👤 Driver Profile Card
Stack(
clipBehavior: Clip.none,
children: [
ClipRRect(
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(8),
bottomRight: Radius.circular(8)),
child: Image.asset(
'images/tanker_image.jpeg', // Replace with your image
height: 200,
width: double.infinity,
fit: BoxFit.cover,
// Main card (give extra top padding so text starts below the avatar)
Container(
padding: EdgeInsets.fromLTRB(
_cardHPad,
_cardTPad + _avSize - _avatarOverlap, // space for avatar
_cardHPad,
16,
),
decoration: const BoxDecoration(
color: Color(0xFFF3F1FB),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// status chip (just under avatar, aligned left)
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 6),
Text(
widget.sourceDetails.source_name,
style: fontTextStyle(
20, Color(0XFF2D2E30), FontWeight.w500),
),
const SizedBox(height: 2),
Text(
"+91 "+widget.sourceDetails.phone,
style: fontTextStyle(
12, Color(0XFF646566), FontWeight.w400),
),
const SizedBox(height: 2),
Text(
"+91 "+widget.sourceDetails.phone,
style: fontTextStyle(
12, Color(0XFF646566), FontWeight.w400),
),
],
),),
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') {
//_openDriverFormSheet(context);
} else if (value == 'disable') {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Disable selected')),
);
} else if (value == 'delete') {
//showDeleteDriverDialog(context);
}
},
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(
'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(
'Delete',
style: fontTextStyle(14, const Color(0xFFE2483D), FontWeight.w400),
),
],
),
),
],
)
],
),
const SizedBox(height: 8),
],
),
),
// Floating avatar (top-left, overlapping the card)
Positioned(
top: 8,
right: 8,
child: IconButton(
icon: const Icon(Icons.more_vert, color: Colors.white),
onPressed: _showMenu,
top: -_avatarOverlap,
left: _cardHPad,
child: ClipRRect(
borderRadius: BorderRadius.circular(_avSize / 2),
child: Image.asset(
'images/avatar.png',
width: _avSize,
height: _avSize,
fit: BoxFit.cover,
),
),
)
),
// Call icon aligned with the cards top-right
/*Positioned(
top: _cardTPad - _avatarOverlap + 4, // aligns near chip row
right: _cardHPad - 4,
child: IconButton(
onPressed: () {
// 📞 Call action
},
icon: const Icon(Icons.call, color: Color(0xFF4F46E5)),
),
),*/
],
),
const SizedBox(height: 12),
Padding(
@ -229,9 +378,6 @@ class _SourceDetailsScreenState extends State<SourceDetailsScreen> {
},
),
const SizedBox(height: 20),
],
),
),
);
],))));
}
}

@ -73,6 +73,295 @@ class _TankerDetailsPageState extends State<TankerDetailsPage> {
String search = '';
bool isLoading = false;
bool isStatusLoading = false;
String? currentAvailability;
@override
void initState() {
super.initState();
_nameCtrl.text=widget.tankerDetails.tanker_name ?? '';
// If the tanker already has availability from backend, prepare it
if (widget.tankerDetails.availability != null &&
widget.tankerDetails.availability is List &&
widget.tankerDetails.availability.length == 2) {
final a = widget.tankerDetails.availability;
currentAvailability = "${a[0]}|${a[1]}";
// Automatically show dropdown bottom sheet after short delay
/*WidgetsBinding.instance.addPostFrameCallback((_) {
_showDropdownBottomSheet(defaultValue: currentAvailability);
});*/
}
}
// Dropdown options: fill + availability combined
final List<String> options = [
"empty|available",
"empty|blocked",
"empty|undermaintanence",
"filled|available",
"filled|blocked",
"filled|undermaintanence",
];
String _getLabel(String value) {
final parts = value.split('|');
return "${parts[0].toUpperCase()}${parts[1].toUpperCase()}";
}
Future<void> _updateAvailability(String selectedValue) async {
// 🔹 Split the combined dropdown value like "filled|available"
final parts = selectedValue.split('|');
final availability = [parts[0], parts[1]];
// 🔹 Build payload for updateTanker API
final payload = <String, dynamic>{
"tankerName": widget.tankerDetails.tanker_name,
"availability": availability, // send array as backend expects
};
try {
setState(() => isStatusLoading = true);
// 🔹 Reuse your existing update API
final bool tankStatus = await AppSettings.updateTankerAvailability(payload);
if (!mounted) return;
if (tankStatus) {
AppSettings.longSuccessToast("Tanker status updated successfully");
// Close sheet and refresh details
Navigator.pop(context, true);
await _refreshTankerDetails();
} else {
Navigator.pop(context, true);
AppSettings.longFailedToast("Tanker status update failed");
}
} catch (e) {
debugPrint("⚠️ update tanker error: $e");
if (!mounted) return;
AppSettings.longFailedToast("Something went wrong: $e");
} finally {
if (mounted) setState(() => isStatusLoading = false);
}
}
void _showDropdownBottomSheet({String? defaultValue}) {
String? selectedValue = defaultValue; // preselect from backend
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent, // nice rounded corners
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
builder: (context) {
final viewInsets = MediaQuery.of(context).viewInsets.bottom;
return FractionallySizedBox(
heightFactor: 0.6, // increase/decrease height here (e.g., 0.7 / 0.8)
child: Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
padding: EdgeInsets.fromLTRB(20, 16, 20, 16 + viewInsets),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// drag handle
Center(
child: Container(
width: 60,
height: 4,
margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration(
color: const Color(0xFFE0E0E0),
borderRadius: BorderRadius.circular(2),
),
),
),
const Text(
"Change Tanker Status",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
),
const SizedBox(height: 20),
// Make central content scrollable within fixed height
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
DropdownButtonFormField<String>(
value: selectedValue,
decoration: InputDecoration(
labelText: "Select Availability",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
contentPadding: const EdgeInsets.symmetric(
vertical: 10,
horizontal: 12,
),
),
items: options
.map((opt) => DropdownMenuItem(
value: opt,
child: Text(_getLabel(opt)),
))
.toList(),
onChanged: (val) => selectedValue = val,
),
const SizedBox(height: 20),
],
),
),
),
// Sticky submit at bottom
SizedBox(
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF515253),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
padding: const EdgeInsets.symmetric(vertical: 12),
),
onPressed: () {
if (selectedValue != null) {
_updateAvailability(selectedValue!);
} else {
AppSettings.longFailedToast("Please select an option");
}
},
child: isLoading
? const SizedBox(
width: 20, height: 20, child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2))
: const Text("Submit"),
),
),
],
),
),
);
},
);
}
showDeleteTankerDialog(BuildContext context) async {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return AlertDialog(
backgroundColor: Color(0XFFFFFFFF),// Set your desired background color
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), // Optional: Rounded corners
),
title: Center(
child: Text('Delete Tanker?' ,style: fontTextStyle(16,Color(0XFF3B3B3B),FontWeight.w600),),
),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Container(
child: Text('Do u want to delete "${widget.tankerDetails.tanker_name}"',style: fontTextStyle(14,Color(0XFF101214),FontWeight.w600),),
),
],
),
),
actions: <Widget>[
Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(child: GestureDetector(
onTap: (){
Navigator.pop(context);
},
child: Container(
decoration: BoxDecoration(
shape: BoxShape.rectangle,
color: Color(0XFFFFFFFF),
border: Border.all(
width: 1,
color: Color(0XFF1D7AFC)),
borderRadius: BorderRadius.circular(
12,
)),
alignment: Alignment.center,
child: Visibility(
visible: true,
child: Padding(
padding: EdgeInsets.fromLTRB(16,12,16,12),
child: Text('Cancel', style: fontTextStyle(12, Color(0XFF1D7AFC), FontWeight.w600)),
),
),
),
),),
SizedBox(width:MediaQuery.of(context).size.width * .016,),
Expanded(child: GestureDetector(
onTap: ()async{
bool status = await AppSettings.deleteTanker(widget.tankerDetails.tanker_name,);
if(status){
AppSettings.longSuccessToast('Tanker deleted successfully');
Navigator.of(context).pop(true);
Navigator.of(context).pop(true);
}
else{
Navigator.of(context).pop(true);
AppSettings.longFailedToast('Failed to delete tanker');
}
},
child: Container(
decoration: BoxDecoration(
shape: BoxShape.rectangle,
color: Color(0XFFE2483D),
border: Border.all(
width: 1,
color: Color(0XFFE2483D)),
borderRadius: BorderRadius.circular(
12,
)),
alignment: Alignment.center,
child: Visibility(
visible: true,
child: Padding(
padding: EdgeInsets.fromLTRB(16,12,16,12),
child: Text(
'Delete',
style: fontTextStyle(
12,
Color(0XFFFFFFFF),
FontWeight.w600)),
),
),
)
),)
],
),
),
],
);
});
},
);
}
Color _chipColor(String s) {
switch (s) {
case 'filled':
@ -596,9 +885,7 @@ class _TankerDetailsPageState extends State<TankerDetailsPage> {
const SnackBar(content: Text('Disable selected')),
);
} else if (value == 'delete') {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Delete selected')),
);
showDeleteTankerDialog(context);
}
},
itemBuilder: (context) => [
@ -682,7 +969,7 @@ class _TankerDetailsPageState extends State<TankerDetailsPage> {
// Or: side: const BorderSide(color: Color(0xFFC3C4C4), width: 1.2),
),
onPressed: () async {
// _showAssignTankerBottomSheet();
_showDropdownBottomSheet(defaultValue: currentAvailability);
},
child: Text(
"Change Status",

Loading…
Cancel
Save