tanker details

master
Sneha 2 months ago
parent 23e9425217
commit 20fcf1e9af

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

@ -0,0 +1,206 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:supplier_new/common/settings.dart';
class DeliveryCalendarScreen extends StatefulWidget {
const DeliveryCalendarScreen({super.key});
@override
State<DeliveryCalendarScreen> createState() => _DeliveryCalendarScreenState();
}
class _DeliveryCalendarScreenState extends State<DeliveryCalendarScreen> {
DateTime _focusedMonth = DateTime(2025, 10);
late List<Map<String, dynamic>> _calendarData;
@override
void initState() {
super.initState();
_calendarData = _generateCalendarData();
}
List<Map<String, dynamic>> _generateCalendarData() {
return [
{"day": 1, "status": "Delivered"},
{"day": 2, "status": "Delivered"},
{"day": 3, "status": "Rescheduled"},
{"day": 4, "status": "Delivered"},
{"day": 5, "status": "Delivered"},
{"day": 6, "status": "Delivered"},
{"day": 7, "status": "Cancelled"},
{"day": 8, "status": "Delivered"},
{"day": 9, "status": "Delivered"},
{"day": 10, "status": "Delivered"},
{"day": 11, "status": "Cancelled"},
{"day": 12, "status": "Delivery"},
{"day": 13, "status": "Delivery"},
{"day": 14, "status": "Delivery"},
{"day": 15, "status": "Delivery"},
{"day": 16, "status": "Delivery"},
{"day": 17, "status": "Delivery"},
{"day": 18, "status": "Delivery"},
{"day": 19, "status": "Delivery"},
{"day": 20, "status": "Delivery"},
{"day": 21, "status": "Delivery"},
{"day": 22, "status": "Delivery"},
{"day": 23, "status": "Delivery"},
{"day": 24, "status": "Delivery"},
{"day": 25, "status": "Delivery"},
{"day": 26, "status": "Delivery"},
];
}
Color _getBackgroundColor(String status) {
switch (status) {
case "Delivered":
return const Color(0xFFE6F4EA);
case "Cancelled":
return const Color(0xFFFDE8E8);
case "Rescheduled":
return const Color(0xFFF2F2F2);
case "Delivery":
return const Color(0xFFEFF4FF);
default:
return Colors.white;
}
}
Color _getTextColor(String status) {
switch (status) {
case "Delivered":
return Colors.green;
case "Cancelled":
return Colors.red;
case "Rescheduled":
return Colors.black54;
case "Delivery":
return const Color(0xFF3B6FE0);
default:
return Colors.black87;
}
}
Widget _getStatusIcon(String status) {
switch (status) {
case "Delivered":
return const Icon(Icons.check, size: 16, color: Colors.green);
case "Cancelled":
return const Icon(Icons.close, size: 16, color: Colors.red);
case "Rescheduled":
return const Icon(Icons.access_time, size: 16, color: Colors.black54);
case "Delivery":
return const Icon(Icons.local_shipping, size: 16, color: Color(0xFF3B6FE0));
default:
return const SizedBox.shrink();
}
}
String _getMonthYear() {
return DateFormat('MMM yyyy').format(_focusedMonth).toUpperCase();
}
int _daysInMonth(DateTime date) {
final firstDayThisMonth = DateTime(date.year, date.month, 1);
final firstDayNextMonth = DateTime(date.year, date.month + 1, 1);
return firstDayNextMonth.difference(firstDayThisMonth).inDays;
}
@override
Widget build(BuildContext context) {
final int totalDays = _daysInMonth(_focusedMonth);
final int firstWeekday = DateTime(_focusedMonth.year, _focusedMonth.month, 1).weekday;
final int totalSlots = totalDays + (firstWeekday - 1);
return 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("Calendar", style: fontTextStyle(16, Colors.black, FontWeight.w600)),
actions: const [
Padding(
padding: EdgeInsets.symmetric(horizontal: 8),
child: Icon(Icons.calendar_month_outlined, color: Color(0xFF8270DB)),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 8),
child: Icon(Icons.notifications_none_rounded, color: Colors.black87),
),
],
),
body: Column(
children: [
const SizedBox(height: 8),
Text(_getMonthYear(), style: fontTextStyle(16, Colors.black, FontWeight.w600)),
const SizedBox(height: 8),
// Weekdays Row
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Row(
children: const [
Expanded(child: Center(child: Text("MON"))),
Expanded(child: Center(child: Text("TUE"))),
Expanded(child: Center(child: Text("WED"))),
Expanded(child: Center(child: Text("THU"))),
Expanded(child: Center(child: Text("FRI"))),
Expanded(child: Center(child: Text("SAT"))),
Expanded(child: Center(child: Text("SUN"))),
],
),
),
const SizedBox(height: 8),
// Calendar Grid
Expanded(
child: GridView.builder(
padding: const EdgeInsets.symmetric(horizontal: 4),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 7,
crossAxisSpacing: 4,
mainAxisSpacing: 4,
),
itemCount: totalSlots,
itemBuilder: (context, index) {
if (index < firstWeekday - 1) {
return const SizedBox.shrink();
}
final day = index - (firstWeekday - 2);
final status = _calendarData
.firstWhere(
(item) => item['day'] == day,
orElse: () => {"status": ""},
)['status']
.toString();
return Container(
decoration: BoxDecoration(
color: _getBackgroundColor(status),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey.shade300),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"$day",
style: fontTextStyle(13, _getTextColor(status), FontWeight.w600),
),
const SizedBox(height: 4),
_getStatusIcon(status),
],
),
);
},
),
),
],
),
);
}
}

@ -19,6 +19,7 @@ import 'package:supplier_new/set_rates/set_rates.dart';
import '../login/login.dart';
import '../resources/resources_fleet.dart';
import '../resources/resources_main.dart';
import 'calander.dart';
class DashboardScreen extends StatefulWidget {
const DashboardScreen({super.key});
@ -318,7 +319,14 @@ class _DashboardScreenState extends State<DashboardScreen> {
padding: const EdgeInsets.fromLTRB(10, 10, 0, 10),
child: IconButton(
icon: Image.asset('images/calendar_appbar.png'),
onPressed: () {},
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DeliveryCalendarScreen(),
),
);
},
),
),
Padding(

@ -23,6 +23,19 @@ TextStyle fontTextStyle(double fontSize,Color fontColor,FontWeight weight) {
);
}
TextStyle fontTextStylewithUnderline(double fontSize,Color fontColor,FontWeight weight, Color underlineColor) {
return GoogleFonts.inter(
textStyle: TextStyle(
fontSize: fontSize,
fontWeight: weight,
color:fontColor,
decoration: TextDecoration.underline,
decorationColor: underlineColor,
decorationThickness: 1,
),
);
}
TextStyle fontTextStyleHeight(double fontSize,Color fontColor,FontWeight weight,double height) {
return GoogleFonts.inter(
textStyle: TextStyle(
@ -147,7 +160,9 @@ class AppSettings{
static String addDriversUrl = host + 'addDeliveryboys';
static String addSourceLocationsUrl = host + 'addSource';
static String getSourceLoctaionsUrl = host + 'getallsourcesofsupplier';
static String setRatesDailyUrl = host + 'tankers';
static String getSupplierDetailsUrl = host + 'suppliers';
static String updatePumpFeeUrl = host + 'suppliers';
static String formDouble(dynamic s) {
@ -525,8 +540,9 @@ class AppSettings{
}
static Future<bool> addDrivers(payload) async {
var response = await http.post(Uri.parse(addDriversUrl + '/' + supplierId),
body: json.encode(payload), headers: await buildRequestHeaders());
var uri =Uri.parse(addDriversUrl + '/' + supplierId);
var response = await http.post(uri, body: json.encode(payload), headers: await buildRequestHeaders());
if (response.statusCode == 200) {
try {
@ -540,8 +556,7 @@ class AppSettings{
} else if (response.statusCode == 401) {
bool status = await AppSettings.resetToken();
if (status) {
response = await http.post(Uri.parse(addTankerUrl + '/' + supplierId),
body: json.encode(payload), headers: await buildRequestHeaders());
response = await http.post(uri, body: json.encode(payload), headers: await buildRequestHeaders());
if (response.statusCode == 200) {
return true;
} else {
@ -580,7 +595,9 @@ class AppSettings{
}
static Future<bool> addSourceLocations(payload) async {
var response = await http.post(Uri.parse(addSourceLocationsUrl + '/' + supplierId),
var uri = Uri.parse(addSourceLocationsUrl + '/' + supplierId);
var response = await http.post(uri,
body: json.encode(payload), headers: await buildRequestHeaders());
if (response.statusCode == 200) {
@ -595,7 +612,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;
@ -610,6 +627,92 @@ class AppSettings{
}
}
static Future<bool> setRatesDaily(payload) async {
var uri = Uri.parse(setRatesDailyUrl + '/' + supplierId+'/update-prices');
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> getSupplierDetails() async {
var uri = Uri.parse(getSupplierDetailsUrl+'/'+supplierId);
//uri = uri.replace(query: 'supplierId=$supplierId');
var response = await http.get(uri, headers: await buildRequestHeaders());
if (response.statusCode == 200) {
return response.body;
} else if (response.statusCode == 401) {
bool status = await AppSettings.resetToken();
if (status) {
response = await http.get(uri, headers: await buildRequestHeaders());
if (response.statusCode == 200) {
return response.body;
} else {
return '';
}
} else {
return '';
}
} else {
return '';
}
}
static Future<bool> updatePumpFee(payload) async {
var uri =Uri.parse(updatePumpFeeUrl + '/' + supplierId+'/pumping-fee');
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;
}
}
/*Apis ends here*/

@ -7,6 +7,9 @@ import 'package:supplier_new/orders/orders_model.dart';
import 'package:supplier_new/orders/search_order_appbar.dart';
import 'package:intl/intl.dart';
import 'assign_driver.dart';
import 'cancel_order.dart';
class AllOrders extends StatefulWidget {
final String navigationFrom;
AllOrders({
@ -86,7 +89,14 @@ class _AllOrdersState extends State<AllOrders> {
print("Help tapped");
},
):null,
body: SingleChildScrollView(
body: isLoading
? const Center(child: CircularProgressIndicator())
: ordersList.isEmpty?Center(
child: Text(
'No Data Available',
style: fontTextStyle(16,Color(0XFF000000),FontWeight.w700),
),
):SingleChildScrollView(
child:
Padding(
padding: EdgeInsets.all(16),
@ -205,7 +215,10 @@ class _AllOrdersState extends State<AllOrders> {
),
// Orders list for this date
...ordersForDate.map((order) => OrderCard(order: order)),
...ordersForDate.map((order) => OrderCard(
order: order,
onRefresh: _fetchOrders, // 👈 pass parent function here
)),
const SizedBox(height: 12),
],
);
@ -276,8 +289,9 @@ class FilterChipWidget extends StatelessWidget {
class OrderCard extends StatelessWidget {
final OrdersModel order;
final VoidCallback? onRefresh;
const OrderCard({super.key, required this.order});
const OrderCard({super.key, required this.order, this.onRefresh});
Color _getStatusColor() {
switch (order.status.toLowerCase()) {
@ -403,27 +417,74 @@ class OrderCard extends StatelessWidget {
style:fontTextStyle(8,Color(0XFF646566),FontWeight.w400)
),
const SizedBox(height: 12),
Visibility(
visible: order.status.toLowerCase().toString()=='advance_paid',
child: GestureDetector(
onTap: (){
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(22),
border: Border.all(color: const Color(0XFF939495)),
),
child: Padding(
padding: EdgeInsets.fromLTRB(8,4,8,4),
child: Text(
"Assign",
style: fontTextStyle(
14, const Color(0XFF515253), FontWeight.w400),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Visibility(
visible: order.status.toLowerCase().toString()=='advance_paid',
child: 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
}
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(22),
border: Border.all(color: const Color(0XFF939495)),
),
child: Padding(
padding: EdgeInsets.fromLTRB(8,4,8,4),
child: Text(
"Assign",
style: fontTextStyle(
14, const Color(0XFF515253), FontWeight.w400),
),
)
),
)
),
),),
),),
Visibility(
visible: order.status.toLowerCase().toString()=='advance_paid',
child: GestureDetector(
onTap: ()async{
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CancelOrderScreen(order: order),
),
);
// If result indicates API reload
if (result == true) {
onRefresh?.call(); // 👈 safe call to refresh parent
}
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(22),
border: Border.all(color: const Color(0XFFE2483D)),
color: Color(0XFFE2483D)
),
child: Padding(
padding: EdgeInsets.fromLTRB(8,4,8,4),
child: Text(
"Cancel",
style: fontTextStyle(
14, const Color(0XFFFFFFFF), FontWeight.w400),
),
)
),
),),
],
)
/*Text(

@ -0,0 +1,693 @@
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';
class AssignDriverScreen extends StatefulWidget {
var order;
var status;
AssignDriverScreen({this.order, this.status});
@override
State<AssignDriverScreen> createState() => _AssignDriverScreenState();
}
class _AssignDriverScreenState extends State<AssignDriverScreen> {
int advancePayable = 0;
int advance = 0;
double amountToPayAfterDelivery = 0.0;
double totalFare = 0.0;
bool isLoading = false;
List<DriversModel> driversList = [];
@override
void initState() {
// TODO: implement initState
super.initState();
_fetchDrivers();
advance = 150;
advancePayable = advance;
totalFare = advance + double.parse(widget.order.quoted_amount);
amountToPayAfterDelivery = totalFare - advancePayable;
}
Future<void> _fetchDrivers() async {
setState(() => isLoading = true);
try {
final response = await AppSettings.getDrivers();
final data = (jsonDecode(response)['data'] as List)
.map((e) => DriversModel.fromJson(e))
.toList();
if (!mounted) return;
setState(() {
driversList = data;
isLoading = false;
});
} catch (e) {
debugPrint("⚠️ Error fetching drivers: $e");
setState(() => isLoading = false);
}
}
void _showAssignDriverBottomSheet() {
int? selectedDriverIndex; // Track selected driver
showModalBottomSheet(
context: context,
isScrollControlled: true,
isDismissible: false,
enableDrag: false,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
builder: (context) {
return WillPopScope( // block Android back button
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(
padding: const EdgeInsets.all(16),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: Container(
width: 60,
height: 4,
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
color: const Color(0xFFE0E0E0),
borderRadius: BorderRadius.circular(2),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Assign Driver",
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),),
)
],
),
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: [
Text(
widget.order.building_name,
style: fontTextStyle(20, const Color(0XFF2D2E30), FontWeight.w600),
),
Text(
widget.order.displayAddress,
style: fontTextStyle(12, const Color(0XFF939495), FontWeight.w400),
),
],
),
const Spacer(),
Text(
'${widget.order.distanceInKm} Km',
style: fontTextStyle(12, const Color(0XFF939495), FontWeight.w400),
),
],
),
),
const SizedBox(height: 16),
Text(
"SELECT DRIVER",
style: fontTextStyle(10, const Color(0XFF2D2E30), FontWeight.w600),
),
const SizedBox(height: 12),
// 🧍 Driver List
Expanded(
child: isLoading
? const Center(child: CircularProgressIndicator())
: ListView.separated(
controller: scrollController,
itemCount: driversList.length,
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
? const Color(0XFF0A9E04)
: (d.status == "on delivery"
? const Color(0XFFD0AE3C)
: (d.status == "offline"
? const Color(0XFF939495)
: Colors.grey));
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(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(
color: isSelected
? const Color(0XFF8270DB)
: const Color(0XFFC3C4C4),
width: 1,
),
),
color: isSelected
? const Color(0XFFEDEBFF)
: Colors.white,
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Row(
children: [
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,
),
),
),
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: statusColor),
),
child: Text(
d.status,
style: fontTextStyle(
10,
statusColor,
FontWeight.w400,
),
),
),
],
),
),
),
),
);
},
),
),
const SizedBox(height: 16),
// 🟣 Assign Button
SizedBox(
width: double.infinity,
child: GestureDetector(
onTap: () async {
if (selectedDriverIndex == null) {
AppSettings.longFailedToast('Please select driver');
return;
}
final selectedDriver = driversList[selectedDriverIndex!];
// Call your API here
// await _assignDriverApi(selectedDriver.driver_id);
if (context.mounted) Navigator.pop(context);
},
child: Container(
decoration: BoxDecoration(
color: const Color(0XFF8270DB),
borderRadius: BorderRadius.circular(24),
),
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(vertical: 12),
child: Text(
'Assign',
style: fontTextStyle(
14, const Color(0XFFFFFFFF), FontWeight.w500),
),
),
),
),
],
),
);
},
);
},
),);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
extendBodyBehindAppBar: true,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
scrolledUnderElevation: 0,
title: Text(
'',
style: fontTextStyle(16, Color(0XFF2A2A2A), FontWeight.w600),
),
iconTheme: IconThemeData(color: Color(0XFF2A2A2A)),
actions: [
Row(
children: [
Padding(
padding: EdgeInsets.fromLTRB(0, 10, 10, 10),
child: IconButton(
icon: Image.asset(
'images/help_appbar.png',
height: 20,
width: 20,
color: Color(0XFFFFFFFF),
),
onPressed: () {},
),
)
],
)
],
leading: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Padding(
padding:
const EdgeInsets.fromLTRB(8, 8, 8, 8), // Add padding if needed
child: Image.asset(
'images/backbutton_appbar.png', // Replace with your image path
fit: BoxFit.contain,
color: Color(0XFFFFFFFF),
height: 24,
width: 24,
),
),
),
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// 🔹 Top Card with Image
Stack(
clipBehavior: Clip.none,
children: [
/// 🔹 Background Image
ClipRRect(
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(0),
bottomRight: Radius.circular(0),
),
child: Image.asset(
"images/building.png",
height: 220,
width: double.infinity,
fit: BoxFit.cover,
),
),
/// 🔹 Floating Info Card (half on image, half below)
Positioned(
bottom: -40, // pulls the card out by 40px
left: 12,
right: 12,
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 6,
offset: Offset(0, 3),
),
],
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/*Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Color(0XFFFFFFFF),
borderRadius: BorderRadius.circular(4),
border: Border.all(
color: widget.status.statusColor,
width: 0.5,
),
),
child: Text(widget.status.status,
style: fontTextStyle(
12,
widget.status.statusColor,
FontWeight.w500)),
),*/
Text(
widget.order.building_name,
style: fontTextStyle(
20, const Color(0XFF2D2E30), FontWeight.w600),
),
SizedBox(height: 4),
Text(
widget.order.displayAddress,
style: fontTextStyle(
12, const Color(0XFF939495), FontWeight.w400),
),
],
),
const Spacer(),
Text(
widget.order.distanceInKm.toString() + 'Km',
style: fontTextStyle(
12, const Color(0XFF939495), FontWeight.w400),
),
],
),
),
),
],
),
SizedBox(
height: MediaQuery.of(context).size.height * .08,
),
/// 🔹 Order Details
Padding(
padding: EdgeInsets.fromLTRB(16, 0, 16, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"ORDER DETAILS",
style: fontTextStyle(
10, const Color(0XFF2D2E30), FontWeight.w600),
),
SizedBox(
height: MediaQuery.of(context).size.height * .011,
),
_detailTwoRow(
"Tanker Price",
"${AppSettings.formDouble(widget.order.quoted_amount) ?? ''}",
"images/financialsBottomIcon.png",
"",
"",
""),
SizedBox(
height: MediaQuery.of(context).size.height * .02,
),
_detailTwoRow(
"Water Type",
"${widget.order.type_of_water}",
"images/water.png",
"Date of Delivery",
"${widget.order.date}",
"images/calendar_appbar.png",
),
SizedBox(
height: MediaQuery.of(context).size.height * .02,
),
_detailTwoRow(
"Capacity",
"${widget.order.capacity}",
"images/capacity.png",
"Time of Delivery",
"${widget.order.time}",
"images/time.png",
),
SizedBox(
height: MediaQuery.of(context).size.height * .02,
),
_detailTwoRow(
"Quantity",
"${widget.order.quantity}",
"images/quantity.png",
"Booking Charges",
advance.toString(),
"images/advance.png",
),
],
),
),
SizedBox(
height: MediaQuery.of(context).size.height * .008,
),
/// 🔹 Additional Details
Padding(
padding: EdgeInsets.fromLTRB(16, 0, 16, 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"ADDITIONAL DETAILS",
style: fontTextStyle(
10, const Color(0XFF2D2E30), FontWeight.w600),
),
SizedBox(
height: MediaQuery.of(context).size.height * .011,
),
Text(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, "
"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "
"Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut "
"aliquip ex ea commodo consequat.",
style: fontTextStyle(
12, const Color(0XFF646566), FontWeight.w400),
),
],
)),
],
),
),
/// 🔹 Bottom Action Buttons
bottomNavigationBar: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: Color(0XFFFFFFFF),
border: Border(top: BorderSide(color: Color(0XFFF5F6F6))),
),
child: Row(
children: [
Expanded(
child: OutlinedButton(
style: OutlinedButton.styleFrom(
foregroundColor: Color(0XFFFFFFFF),
backgroundColor: Colors.white,
side: BorderSide(color: Color(0XFFFFFFFF)),
padding: EdgeInsets.symmetric(vertical: 10),
),
onPressed: () async {
AppSettings.preLoaderDialog(context);
bool isOnline = await AppSettings.internetConnectivity();
if (isOnline) {
var payload = new Map<String, dynamic>();
payload["supplierId"] = AppSettings.supplierId;
payload["amount"] = int.parse(widget.order.quoted_amount);
payload["delivery_charges"] = advance;
payload["action"] = 'reject';
bool status = await AppSettings.acceptOrderRequests(
payload, widget.order.dbId);
try {
if (status) {
Navigator.of(context, rootNavigator: true).pop();
AppSettings.longSuccessToast(
"Order request rejected Successfully");
Navigator.pop(context, true);
} else {
Navigator.of(context, rootNavigator: true).pop();
AppSettings.longFailedToast(
"reject of order request Failed");
}
} catch (e) {
Navigator.of(context, rootNavigator: true).pop();
print(e);
}
} else {
Navigator.of(context, rootNavigator: true).pop();
AppSettings.longFailedToast("Please Check internet");
}
},
child: Text(
"CANCEL",
style: fontTextStyle(
14, const Color(0XFFE2483D), FontWeight.w400),
),
),
),
SizedBox(width: 8),
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Color(0XFF8270DB),
foregroundColor: Color(0XFFFFFFFF),
padding: EdgeInsets.symmetric(vertical: 10),
),
onPressed: () async {
_showAssignDriverBottomSheet();
},
child: Text(
"Assign Driver",
style: fontTextStyle(
14, const Color(0XFFFFFFFF), FontWeight.w400),
),
),
),
],
)),
);
}
/// 🔹 Helper widget for rows
Widget _detailRow(String title, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: fontTextStyle(12, const Color(0XFF646566), FontWeight.w400),
),
Text(
value,
style: fontTextStyle(12, const Color(0XFF2D2E30), FontWeight.w500),
),
],
),
);
}
Widget _detailTwoRow(
String title1,
String value1,
String path1,
String title2,
String value2,
String path2, {
EdgeInsetsGeometry padding = const EdgeInsets.symmetric(vertical: 6),
}) {
final titleStyle = fontTextStyle(12, Color(0XFF646566), FontWeight.w400);
final valueStyle = fontTextStyle(12, Color(0XFF343637), FontWeight.w500);
Widget _col(String t, String v, String path) {
return Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (path.isNotEmpty)
Image.asset(
path,
fit: BoxFit.contain,
height: 20,
width: 20,
color: const Color(0XFFC3C4C4),
),
if (path.isNotEmpty) const SizedBox(width: 6),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(t,
style: titleStyle,
maxLines: 1,
overflow: TextOverflow.ellipsis),
Text(v,
style: valueStyle,
maxLines: 1,
overflow: TextOverflow.ellipsis),
],
),
)
],
),
),
);
}
return Padding(
padding: padding,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_col(title1, value1, path1),
_col(title2, value2, path2),
],
),
);
}
}

@ -0,0 +1,179 @@
import 'package:flutter/material.dart';
import '../common/settings.dart';
class CancelOrderScreen extends StatefulWidget {
var order;
var status;
CancelOrderScreen({this.order, this.status});
@override
State<CancelOrderScreen> createState() => _CancelOrderScreenState();
}
class _CancelOrderScreenState extends State<CancelOrderScreen> {
String? selectedReason;
final TextEditingController reasonController = TextEditingController();
final List<String> reasons = [
"Changed my mind",
"Tanker not available",
"Vehicle got damaged",
"Buyer asked me to cancel",
"Other",
];
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
scrolledUnderElevation: 0,
title: Text(
'Cancel Order',
style: fontTextStyle(14, Color(0XFF2A2A2A), FontWeight.w500),
),
iconTheme: IconThemeData(color: Color(0XFF2A2A2A)),
leading: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Padding(
padding:
const EdgeInsets.fromLTRB(8, 8, 8, 8), // Add padding if needed
child: Image.asset(
'images/backbutton_appbar.png', // Replace with your image path
fit: BoxFit.contain,
color: Color(0XFF2A2A2A),
height: 24,
width: 24,
),
),
),
),
body: Padding(
padding:EdgeInsets.all(24.0),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Why are you cancelling?",
style: fontTextStyle(14, Color(0XFF2A2A2A), FontWeight.w600),
),
SizedBox(height:MediaQuery.of(context).size.height * .008,),
// Radio buttons
...reasons.map((reason) {
return RadioListTile<String>(
value: reason,
groupValue: selectedReason,
activeColor: primaryColor,
contentPadding: EdgeInsets.zero,
title: Text(
reason,
style:
fontTextStyle(12, Color(0XFF2D2E30), FontWeight.w400),
),
onChanged: (value) {
setState(() {
selectedReason = value;
});
},
);
}).toList(),
if (selectedReason == "Other")
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: TextFormField(
controller: reasonController,
maxLines: 5,
style: fontTextStyle(14, Color(0XFF101214), FontWeight.w400),
cursorColor: Color(0XFF1D7AFC),
textCapitalization: TextCapitalization.sentences,
decoration: textFormFieldDecorationHintText(
Icons.phone,
'Describe your reason..',
),
),
),
SizedBox(height:MediaQuery.of(context).size.height * .008,),
Text(
"Cancellation Policy",
style:fontTextStyle(14, Color(0XFF2A2A2A), FontWeight.w600),
),
SizedBox(height:MediaQuery.of(context).size.height * .016,),
Text(
'Cancel anytime before delivery starts. If its already on the way, a ₹100 cancellation fee may apply. Please review our full terms and conditions for more details.',
style:fontTextStyle(12, Color(0XFF2A2A2A), FontWeight.w400),
),
SizedBox(height:MediaQuery.of(context).size.height * .016,),
GestureDetector(
onTap: () {
// open terms page
},
child: Text(
"View Terms & Conditions",
style: fontTextStylewithUnderline(10, Color(0XFF4692FD), FontWeight.w400,Color(0XFF4692FD)),
),
),
],
),
),
),
bottomNavigationBar: SafeArea(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Row(
children: [
// Secondary
Expanded(
child: OutlinedButton(
onPressed: () => Navigator.pop(context),
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Color(0xFF2A2A2A)),
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
),
child: Text(
"Dont cancel",
style: fontTextStyle(14, const Color(0xFF2A2A2A), FontWeight.w600),
),
),
),
const SizedBox(width: 12),
// Primary
Expanded(
child: ElevatedButton(
onPressed: () {
// TODO: call cancel API
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0XFFE2483D),
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
),
child: Text(
"Cancel Order",
style: fontTextStyle(14, const Color(0XFFFFFFFF), FontWeight.w600),
),
),
),
],
),
),
),
);
}
}

@ -0,0 +1,730 @@
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';
class ChangeDriverScreen extends StatefulWidget {
var order;
var status;
ChangeDriverScreen({this.order, this.status});
@override
State<ChangeDriverScreen> createState() => _ChangeDriverScreenState();
}
class _ChangeDriverScreenState extends State<ChangeDriverScreen> {
int advancePayable = 0;
int advance = 0;
double amountToPayAfterDelivery = 0.0;
double totalFare = 0.0;
bool isLoading = false;
List<DriversModel> driversList = [];
@override
void initState() {
// TODO: implement initState
super.initState();
_fetchDrivers();
advance = 150;
advancePayable = advance;
totalFare = advance + double.parse(widget.order.quoted_amount);
amountToPayAfterDelivery = totalFare - advancePayable;
}
Future<void> _fetchDrivers() async {
setState(() => isLoading = true);
try {
final response = await AppSettings.getDrivers();
final data = (jsonDecode(response)['data'] as List)
.map((e) => DriversModel.fromJson(e))
.toList();
if (!mounted) return;
setState(() {
driversList = data;
isLoading = false;
});
} catch (e) {
debugPrint("⚠️ Error fetching drivers: $e");
setState(() => isLoading = false);
}
}
void _showAssignDriverBottomSheet() {
int? selectedDriverIndex; // Track selected driver
showModalBottomSheet(
context: context,
isScrollControlled: true,
isDismissible: false,
enableDrag: false,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
builder: (context) {
return WillPopScope( // block Android back button
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(
padding: const EdgeInsets.all(16),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: Container(
width: 60,
height: 4,
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
color: const Color(0xFFE0E0E0),
borderRadius: BorderRadius.circular(2),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Assign Driver",
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),),
)
],
),
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: [
Text(
widget.order.building_name,
style: fontTextStyle(20, const Color(0XFF2D2E30), FontWeight.w600),
),
Text(
widget.order.displayAddress,
style: fontTextStyle(12, const Color(0XFF939495), FontWeight.w400),
),
],
),
const Spacer(),
Text(
'${widget.order.distanceInKm} Km',
style: fontTextStyle(12, const Color(0XFF939495), FontWeight.w400),
),
],
),
),
const SizedBox(height: 16),
Text(
"SELECT DRIVER",
style: fontTextStyle(10, const Color(0XFF2D2E30), FontWeight.w600),
),
const SizedBox(height: 12),
// 🧍 Driver List
Expanded(
child: isLoading
? const Center(child: CircularProgressIndicator())
: ListView.separated(
controller: scrollController,
itemCount: driversList.length,
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
? const Color(0XFF0A9E04)
: (d.status == "on delivery"
? const Color(0XFFD0AE3C)
: (d.status == "offline"
? const Color(0XFF939495)
: Colors.grey));
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(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(
color: isSelected
? const Color(0XFF8270DB)
: const Color(0XFFC3C4C4),
width: 1,
),
),
color: isSelected
? const Color(0XFFEDEBFF)
: Colors.white,
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Row(
children: [
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,
),
),
),
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: statusColor),
),
child: Text(
d.status,
style: fontTextStyle(
10,
statusColor,
FontWeight.w400,
),
),
),
],
),
),
),
),
);
},
),
),
const SizedBox(height: 16),
// 🟣 Assign Button
SizedBox(
width: double.infinity,
child: GestureDetector(
onTap: () async {
if (selectedDriverIndex == null) {
AppSettings.longFailedToast('Please select driver');
return;
}
final selectedDriver = driversList[selectedDriverIndex!];
// Call your API here
// await _assignDriverApi(selectedDriver.driver_id);
if (context.mounted) Navigator.pop(context);
},
child: Container(
decoration: BoxDecoration(
color: const Color(0XFF8270DB),
borderRadius: BorderRadius.circular(24),
),
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(vertical: 12),
child: Text(
'Assign',
style: fontTextStyle(
14, const Color(0XFFFFFFFF), FontWeight.w500),
),
),
),
),
],
),
);
},
);
},
),);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
extendBodyBehindAppBar: true,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
scrolledUnderElevation: 0,
title: Text(
'',
style: fontTextStyle(16, Color(0XFF2A2A2A), FontWeight.w600),
),
iconTheme: IconThemeData(color: Color(0XFF2A2A2A)),
actions: [
Row(
children: [
Padding(
padding: EdgeInsets.fromLTRB(0, 10, 10, 10),
child: IconButton(
icon: Image.asset(
'images/help_appbar.png',
height: 20,
width: 20,
color: Color(0XFFFFFFFF),
),
onPressed: () {},
),
)
],
)
],
leading: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Padding(
padding:
const EdgeInsets.fromLTRB(8, 8, 8, 8), // Add padding if needed
child: Image.asset(
'images/backbutton_appbar.png', // Replace with your image path
fit: BoxFit.contain,
color: Color(0XFFFFFFFF),
height: 24,
width: 24,
),
),
),
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// 🔹 Top Card with Image
Stack(
clipBehavior: Clip.none,
children: [
/// 🔹 Background Image
ClipRRect(
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(0),
bottomRight: Radius.circular(0),
),
child: Image.asset(
"images/building.png",
height: 220,
width: double.infinity,
fit: BoxFit.cover,
),
),
/// 🔹 Floating Info Card (half on image, half below)
Positioned(
bottom: -40, // pulls the card out by 40px
left: 12,
right: 12,
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 6,
offset: Offset(0, 3),
),
],
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/*Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Color(0XFFFFFFFF),
borderRadius: BorderRadius.circular(4),
border: Border.all(
color: widget.status.statusColor,
width: 0.5,
),
),
child: Text(widget.status.status,
style: fontTextStyle(
12,
widget.status.statusColor,
FontWeight.w500)),
),*/
Text(
widget.order.building_name,
style: fontTextStyle(
20, const Color(0XFF2D2E30), FontWeight.w600),
),
SizedBox(height: 4),
Text(
widget.order.displayAddress,
style: fontTextStyle(
12, const Color(0XFF939495), FontWeight.w400),
),
],
),
const Spacer(),
Text(
widget.order.distanceInKm.toString() + 'Km',
style: fontTextStyle(
12, const Color(0XFF939495), FontWeight.w400),
),
],
),
),
),
],
),
SizedBox(
height: MediaQuery.of(context).size.height * .08,
),
/// 🔹 Order Details
Padding(
padding: EdgeInsets.fromLTRB(16, 0, 16, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"ORDER DETAILS",
style: fontTextStyle(
10, const Color(0XFF2D2E30), FontWeight.w600),
),
SizedBox(
height: MediaQuery.of(context).size.height * .011,
),
_detailTwoRow(
"Tanker Price",
"${AppSettings.formDouble(widget.order.quoted_amount) ?? ''}",
"images/financialsBottomIcon.png",
"",
"",
""),
SizedBox(
height: MediaQuery.of(context).size.height * .02,
),
_detailTwoRow(
"Water Type",
"${widget.order.type_of_water}",
"images/water.png",
"Date of Delivery",
"${widget.order.date}",
"images/calendar_appbar.png",
),
SizedBox(
height: MediaQuery.of(context).size.height * .02,
),
_detailTwoRow(
"Capacity",
"${widget.order.capacity}",
"images/capacity.png",
"Time of Delivery",
"${widget.order.time}",
"images/time.png",
),
SizedBox(
height: MediaQuery.of(context).size.height * .02,
),
_detailTwoRow(
"Quantity",
"${widget.order.quantity}",
"images/quantity.png",
"Booking Charges",
advance.toString(),
"images/advance.png",
),
],
),
),
SizedBox(
height: MediaQuery.of(context).size.height * .008,
),
/// 🔹 Additional Details
Padding(
padding: EdgeInsets.fromLTRB(16, 0, 16, 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"ADDITIONAL DETAILS",
style: fontTextStyle(
10, const Color(0XFF2D2E30), FontWeight.w600),
),
SizedBox(
height: MediaQuery.of(context).size.height * .011,
),
Text(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, "
"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "
"Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut "
"aliquip ex ea commodo consequat.",
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,
),
),
),
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),
),
child: Text(
'd.status',
style: fontTextStyle(
10,
Colors.green,
FontWeight.w400,
),
),
),
],
)),
],
),
),
/// 🔹 Bottom Action Buttons
bottomNavigationBar: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: Color(0XFFFFFFFF),
border: Border(top: BorderSide(color: Color(0XFFF5F6F6))),
),
child: Row(
children: [
Expanded(
child: OutlinedButton(
style: OutlinedButton.styleFrom(
foregroundColor: Color(0XFFFFFFFF),
backgroundColor: Colors.white,
side: BorderSide(color: Color(0XFFFFFFFF)),
padding: EdgeInsets.symmetric(vertical: 10),
),
onPressed: () async {
AppSettings.preLoaderDialog(context);
bool isOnline = await AppSettings.internetConnectivity();
if (isOnline) {
var payload = new Map<String, dynamic>();
payload["supplierId"] = AppSettings.supplierId;
payload["amount"] = int.parse(widget.order.quoted_amount);
payload["delivery_charges"] = advance;
payload["action"] = 'reject';
bool status = await AppSettings.acceptOrderRequests(
payload, widget.order.dbId);
try {
if (status) {
Navigator.of(context, rootNavigator: true).pop();
AppSettings.longSuccessToast(
"Order request rejected Successfully");
Navigator.pop(context, true);
} else {
Navigator.of(context, rootNavigator: true).pop();
AppSettings.longFailedToast(
"reject of order request Failed");
}
} catch (e) {
Navigator.of(context, rootNavigator: true).pop();
print(e);
}
} else {
Navigator.of(context, rootNavigator: true).pop();
AppSettings.longFailedToast("Please Check internet");
}
},
child: Text(
"CANCEL",
style: fontTextStyle(
14, const Color(0XFFE2483D), FontWeight.w400),
),
),
),
SizedBox(width: 8),
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Color(0XFF8270DB),
foregroundColor: Color(0XFFFFFFFF),
padding: EdgeInsets.symmetric(vertical: 10),
),
onPressed: () async {
_showAssignDriverBottomSheet();
},
child: Text(
"Change Order",
style: fontTextStyle(
14, const Color(0XFFFFFFFF), FontWeight.w400),
),
),
),
],
)),
);
}
/// 🔹 Helper widget for rows
Widget _detailRow(String title, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: fontTextStyle(12, const Color(0XFF646566), FontWeight.w400),
),
Text(
value,
style: fontTextStyle(12, const Color(0XFF2D2E30), FontWeight.w500),
),
],
),
);
}
Widget _detailTwoRow(
String title1,
String value1,
String path1,
String title2,
String value2,
String path2, {
EdgeInsetsGeometry padding = const EdgeInsets.symmetric(vertical: 6),
}) {
final titleStyle = fontTextStyle(12, Color(0XFF646566), FontWeight.w400);
final valueStyle = fontTextStyle(12, Color(0XFF343637), FontWeight.w500);
Widget _col(String t, String v, String path) {
return Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (path.isNotEmpty)
Image.asset(
path,
fit: BoxFit.contain,
height: 20,
width: 20,
color: const Color(0XFFC3C4C4),
),
if (path.isNotEmpty) const SizedBox(width: 6),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(t,
style: titleStyle,
maxLines: 1,
overflow: TextOverflow.ellipsis),
Text(v,
style: valueStyle,
maxLines: 1,
overflow: TextOverflow.ellipsis),
],
),
)
],
),
),
);
}
return Padding(
padding: padding,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_col(title1, value1, path1),
_col(title2, value2, path2),
],
),
);
}
}

@ -15,12 +15,40 @@ class OrderRequestsPage extends StatefulWidget {
class _OrderRequestsPageState extends State<OrderRequestsPage> {
final TextEditingController searchController = TextEditingController();
List<OrderRequestsModel> orderRequestsList = [];
List<OrderRequestsModel> filteredList = [];
bool isLoading = false;
@override
void initState() {
super.initState();
_fetchOrders();
// 🔹 Add listener for search
searchController.addListener(() {
_filterOrders(searchController.text);
});
}
void _filterOrders(String query) {
final lowerQuery = query.toLowerCase();
setState(() {
if (lowerQuery.isEmpty) {
filteredList = orderRequestsList;
} else {
filteredList = orderRequestsList.where((order) {
final title = order.building_name?.toLowerCase() ?? '';
final address = order.displayAddress?.toLowerCase() ?? '';
final waterType = order.type_of_water?.toLowerCase() ?? '';
final capacity = order.capacity?.toLowerCase() ?? '';
return title.contains(lowerQuery) ||
address.contains(lowerQuery) ||
waterType.contains(lowerQuery) ||
capacity.contains(lowerQuery);
}).toList();
}
});
}
Future<void> _fetchOrders() async {
@ -34,6 +62,7 @@ class _OrderRequestsPageState extends State<OrderRequestsPage> {
if (!mounted) return;
setState(() {
orderRequestsList = data;
filteredList = data;
isLoading = false;
});
} catch (e) {
@ -72,8 +101,8 @@ class _OrderRequestsPageState extends State<OrderRequestsPage> {
if (difference.inMinutes < 2) {
status = "New";
color = const Color(0XFF1D7AFC); // Blue
} else if (difference.inMinutes < 30) {
final remaining = 30 - difference.inMinutes;
} else if (difference.inMinutes < 300) {
final remaining = 300 - difference.inMinutes;
status = "Expires in ${remaining}m"; // show time for pending
color = difference.inMinutes < 10
? const Color(0XFFE56910)
@ -111,10 +140,15 @@ class _OrderRequestsPageState extends State<OrderRequestsPage> {
Expanded(
child: isLoading
? const Center(child: CircularProgressIndicator())
: ListView.builder(
itemCount: orderRequestsList.length,
: filteredList .isEmpty?Center(
child: Text(
'No Data Available',
style: fontTextStyle(16,Color(0XFF000000),FontWeight.w700),
),
):ListView.builder(
itemCount: filteredList .length,
itemBuilder: (context, index) {
final order = orderRequestsList[index];
final order = filteredList [index];
final statusMap = getOrderStatus(order.time ?? "", order.status ?? "");
final status = statusMap['status'];
final color = statusMap['color'];
@ -213,6 +247,7 @@ class _OrderRequestsPageState extends State<OrderRequestsPage> {
},
)
),
const SizedBox(width: 5),
Container(
height: 42,
width: 42,

@ -30,7 +30,7 @@ class OrdersModel {
rtvm.building_name = json['buildingName'] ?? '';
rtvm.dbId = json['_id']?? '';
rtvm.address = json['address'] ?? '';
rtvm.type_of_water = json['typeofwater '] ?? '';
rtvm.type_of_water = json['typeofwater'] ?? '';
rtvm.capacity = json['capacity'] ?? '';
rtvm.quantity = json['quantity']?? '';
rtvm.time = json['time'] ?? '';

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:supplier_new/common/settings.dart';
import 'package:supplier_new/plans/plan_details.dart';
import 'package:supplier_new/plans/plan_requests.dart';
import 'package:supplier_new/plans/plans_model.dart';
import 'package:supplier_new/plans/search_plan_appbar.dart';
@ -286,122 +287,132 @@ class PlansCard extends StatelessWidget {
Widget build(BuildContext context) {
bool isActive = delivery.status == "Active";
return Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFFE5E5E5)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.03),
blurRadius: 5,
offset: const Offset(0, 2),
)
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Status Chip
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: isActive ? const Color(0xFFE9F9EE) : const Color(0xFFF2F2F2),
borderRadius: BorderRadius.circular(6),
border: Border.all(
color: isActive ? const Color(0xFF3BB273) : Colors.grey.shade400,
),
),
child: Text(
delivery.status,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: isActive ? const Color(0xFF3BB273) : Colors.grey.shade600,
),
),
),
const SizedBox(height: 8),
// Apartment + Price Row
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
delivery.apartment,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: Color(0xFF2A2A2A),
),
),
Text(
delivery.price,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: Color(0xFF2A2A2A),
),
),
],
return GestureDetector(
onTap: (){
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PlanDetails(),
),
const SizedBox(height: 4),
// Liters & Advance
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
delivery.liters,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xFF7B5AF4),
);
},
child: Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFFE5E5E5)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.03),
blurRadius: 5,
offset: const Offset(0, 2),
)
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Status Chip
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: isActive ? const Color(0xFFE9F9EE) : const Color(0xFFF2F2F2),
borderRadius: BorderRadius.circular(6),
border: Border.all(
color: isActive ? const Color(0xFF3BB273) : Colors.grey.shade400,
),
),
Text(
delivery.advance,
style: const TextStyle(
child: Text(
delivery.status,
style: TextStyle(
fontSize: 12,
color: Color(0xFF646566),
fontWeight: FontWeight.w500,
color: isActive ? const Color(0xFF3BB273) : Colors.grey.shade600,
),
),
],
),
const SizedBox(height: 12),
// Deliveries row with background
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
decoration: BoxDecoration(
color: const Color(0xFFF8F6FF),
borderRadius: BorderRadius.circular(8),
),
child: Row(
const SizedBox(height: 8),
// Apartment + Price Row
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
delivery.deliveries,
delivery.apartment,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
fontSize: 15,
fontWeight: FontWeight.w600,
color: Color(0xFF2A2A2A),
),
),
Text(
delivery.frequency,
delivery.price,
style: const TextStyle(
fontSize: 13,
fontSize: 15,
fontWeight: FontWeight.w600,
color: Color(0xFF2A2A2A),
),
),
],
),
const SizedBox(height: 4),
// Liters & Advance
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
delivery.liters,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xFF7B5AF4),
),
),
Text(
delivery.advance,
style: const TextStyle(
fontSize: 12,
color: Color(0xFF646566),
),
),
],
),
),
],
const SizedBox(height: 12),
// Deliveries row with background
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
decoration: BoxDecoration(
color: const Color(0xFFF8F6FF),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
delivery.deliveries,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: Color(0xFF2A2A2A),
),
),
Text(
delivery.frequency,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: Color(0xFF7B5AF4),
),
),
],
),
),
],
),
),
);
}

@ -0,0 +1,310 @@
import 'package:flutter/material.dart';
import '../common/settings.dart';
class PlanDetails extends StatefulWidget {
const PlanDetails({super.key});
@override
State<PlanDetails> createState() => _PlanDetailsState();
}
class _PlanDetailsState extends State<PlanDetails> {
final List<Map<String, dynamic>> deliveries = [
{
"status": "Pending",
"quantity": "10,000 L",
"type": "Drinking water",
"time": "12:30 AM, Tomorrow",
"button": "Assign Tanker",
},
{
"status": "Pending",
"quantity": "10,000 L",
"type": "Drinking water",
"time": "12:30 AM, Tomorrow",
"driver": "TS 04 J 8394",
"button": "Assign Driver",
},
];
Widget _buildInfoCard(String value, String label, {Color? color}) {
return Expanded(
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: color ?? const Color(0xFFF7F7F7),
borderRadius: BorderRadius.circular(12),
),
child: Column(
children: [
Text(value, style: fontTextStyle(18, Colors.black, FontWeight.w600)),
const SizedBox(height: 4),
Text(label, style: fontTextStyle(13, Colors.black54, FontWeight.w400)),
],
),
),
);
}
Widget _buildFilterChip(String label) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
border: Border.all(color: const Color(0xFFE0E0E0)),
borderRadius: BorderRadius.circular(16),
color: Colors.white,
),
child: Text(
label,
style: fontTextStyle(13, Colors.black87, FontWeight.w500),
),
),
);
}
Widget _buildDeliveryCard(Map<String, dynamic> delivery) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: const Color(0xFFE0E0E0)),
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding:
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: const Color(0xFFFFF2E0),
borderRadius: BorderRadius.circular(8),
),
child: Text(
delivery['status'],
style: fontTextStyle(
12, const Color(0xFFE6882C), FontWeight.w500),
),
),
const Spacer(),
Text(delivery['time'],
style: fontTextStyle(12, Colors.black54, FontWeight.w400)),
],
),
const SizedBox(height: 8),
Text("${delivery['quantity']} - ${delivery['type']}",
style: fontTextStyle(14, Colors.black, FontWeight.w600)),
const SizedBox(height: 8),
if (delivery.containsKey('driver'))
Row(
children: [
const Icon(Icons.local_shipping_outlined,
color: Color(0xFF8270DB), size: 18),
const SizedBox(width: 6),
Text(
delivery['driver'],
style: fontTextStyle(13, Colors.black87, FontWeight.w500),
)
],
),
const SizedBox(height: 8),
Align(
alignment: Alignment.centerRight,
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF8270DB),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
padding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
),
child: Text(
delivery['button'],
style: fontTextStyle(13, Colors.white, FontWeight.w600),
),
),
)
],
),
);
}
@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_new, color: Colors.black),
onPressed: () => Navigator.pop(context),
),
title: Text("Green Valley Apartments",
style: fontTextStyle(16, Colors.black, FontWeight.w600)),
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Image with status
Stack(
children: [
Image.asset(
'images/building.png', // replace with your asset
height: 180,
width: double.infinity,
fit: BoxFit.cover,
),
Positioned(
top: 16,
left: 16,
child: Container(
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: Colors.green.withOpacity(0.9),
borderRadius: BorderRadius.circular(20),
),
child: Text("Active",
style: fontTextStyle(12, Colors.white, FontWeight.w600)),
),
),
],
),
const SizedBox(height: 12),
// Apartment Info
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: [
Text("Green Valley Apartments",
style: fontTextStyle(18, Colors.black, FontWeight.w600)),
const SizedBox(height: 4),
Text("Gacchibowli, Hyderabad",
style: fontTextStyle(14, Colors.black54, FontWeight.w400)),
const SizedBox(height: 4),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.water_drop, color: Color(0xFF8270DB), size: 18),
const SizedBox(width: 4),
Text("Drinking Water",
style: fontTextStyle(14, const Color(0xFF8270DB), FontWeight.w500)),
],
),
const SizedBox(height: 4),
Text("25 June 2025 • 24 deliveries",
style: fontTextStyle(13, Colors.black54, FontWeight.w400)),
],
),
),
const SizedBox(height: 16),
// Quantity & Balance
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
_buildInfoCard("10K", "Quantity"),
const SizedBox(width: 12),
_buildInfoCard("24k", "Balance", color: const Color(0xFFEFF8F1)),
],
),
),
const SizedBox(height: 16),
// Schedule | Pending | Rescheduled
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
_buildInfoCard("3/week", "Schedule"),
const SizedBox(width: 12),
_buildInfoCard("14", "Pending"),
const SizedBox(width: 12),
_buildInfoCard("2", "Rescheduled"),
],
),
),
const SizedBox(height: 20),
// Filter chips
SingleChildScrollView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
children: [
_buildFilterChip("Status"),
_buildFilterChip("Date"),
_buildFilterChip("Quantity"),
_buildFilterChip("Water Type"),
],
),
),
const SizedBox(height: 16),
// Delivery Cards
...deliveries.map((d) => _buildDeliveryCard(d)).toList(),
const SizedBox(height: 24),
// Bottom Buttons
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: () {},
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Color(0xFF8270DB)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
padding: const EdgeInsets.symmetric(vertical: 14),
),
child: Text("Edit Plan",
style: fontTextStyle(
14, const Color(0xFF8270DB), FontWeight.w600)),
),
),
const SizedBox(width: 12),
Expanded(
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFE2483D),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
padding: const EdgeInsets.symmetric(vertical: 14),
),
child: Text("Discontinue",
style: fontTextStyle(
14, Colors.white, FontWeight.w600)),
),
),
],
),
),
const SizedBox(height: 24),
],
),
),
);
}
}

@ -0,0 +1,311 @@
import 'package:flutter/material.dart';
class DriverDetailsPage extends StatefulWidget {
var driverDetails;
var status;
DriverDetailsPage({this.driverDetails, this.status});
@override
State<DriverDetailsPage> createState() => _DriverDetailsPageState();
}
class _DriverDetailsPageState extends State<DriverDetailsPage> {
@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),
),
title: Text(
widget.driverDetails.driver_name.isNotEmpty
? widget.driverDetails.driver_name[0].toUpperCase() +
widget.driverDetails.driver_name.substring(1)
: '',
style: TextStyle(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
centerTitle: false,
actions: [
TextButton(
onPressed: () {},
child: const Text(
"HELP",
style: TextStyle(
color: Color(0xFF4F46E5),
fontWeight: FontWeight.w600,
fontSize: 14,
),
),
),
],
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 👤 Driver Profile Card
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFFF4F0FF),
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const CircleAvatar(
radius: 30,
backgroundColor: Colors.grey,
backgroundImage: AssetImage('images/avatar.png'),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Status Chip
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: const Color(0xFFE8FFF0),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: const Color(0xFF0A9E04), width: 0.8),
),
child: const Text(
"available",
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: Color(0xFF0A9E04),
),
),
),
const SizedBox(height: 4),
Text(
widget.driverDetails.driver_name,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: Colors.black,
),
),
const SizedBox(height: 2),
const Text(
"+91 789456212 • +91 789456212",
style: TextStyle(
fontSize: 13,
color: Colors.black54,
),
),
],
),
),
IconButton(
onPressed: () {
// 📞 Call action
},
icon: const Icon(Icons.call, color: Color(0xFF4F46E5)),
)
],
),
const SizedBox(height: 16),
// Buttons
Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: () {},
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Color(0xFF4F46E5)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
padding: const EdgeInsets.symmetric(vertical: 12),
),
child: const Text(
"View Schedule",
style: TextStyle(
color: Color(0xFF4F46E5),
fontWeight: FontWeight.w600,
),
),
),
),
const SizedBox(width: 12),
Expanded(
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF4F46E5),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
padding: const EdgeInsets.symmetric(vertical: 12),
),
child: const Text(
"Assign",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
),
),
],
),
],
),
),
const SizedBox(height: 24),
// 🪪 License Card
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Container(
width: double.infinity,
decoration: const BoxDecoration(
color: Colors.black,
),
child: Stack(
children: [
// background pattern
/*Image.asset(
'images/license_bg.png',
fit: BoxFit.cover,
width: double.infinity,
height: 140,
),*/
Container(
height: 140,
width: double.infinity,
padding: const EdgeInsets.all(16),
color: Colors.black.withOpacity(0.3),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"DRIVING LICENSE",
style: TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
Spacer(),
Text(
widget.driverDetails.driver_name,
style: TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
Text(
"TS84996859930326",
style: TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
Align(
alignment: Alignment.bottomRight,
child: Text(
"Expires on 29/02/2028",
style: TextStyle(
color: Colors.white70,
fontSize: 12,
),
),
),
],
),
),
],
),
),
),
const SizedBox(height: 24),
const Text(
"RECENT TRIPS",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
const SizedBox(height: 12),
_buildTripCard("Drinking Water - 10,000 L", "7:02 PM, 28 Jun 2025"),
const SizedBox(height: 8),
_buildTripCard("Drinking Water - 10,000 L", "7:02 PM, 28 Jun 2025"),
const SizedBox(height: 8),
_buildTripCard("Drinking Water - 10,000 L", "7:02 PM, 28 Jun 2025"),
const SizedBox(height: 30),
],
),
),
),
);
}
Widget _buildTripCard(String title, String time) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFFF8F8F8),
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
const Icon(Icons.local_shipping, color: Color(0xFF4F46E5), size: 24),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
const SizedBox(height: 2),
Text(
time,
style: const TextStyle(
fontSize: 12,
color: Colors.black54,
),
),
],
),
),
],
),
);
}
}

@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:supplier_new/common/settings.dart';
import 'package:supplier_new/resources/driver_details.dart';
import 'package:supplier_new/resources/drivers_model.dart';
class FirstCharUppercaseFormatter extends TextInputFormatter {
@ -520,12 +521,22 @@ class _ResourcesDriverScreenState extends State<ResourcesDriverScreen> {
separatorBuilder: (_, __) => const SizedBox(height: 12),
itemBuilder: (context, idx) {
final d = driversList[idx];
return DriverCard(
name: d.driver_name,
status: d.status,
location: d.address,
deliveries: int.tryParse(d.deliveries) ?? 0,
commission: d.commision,
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,
),
);
},
),

@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:supplier_new/common/settings.dart';
import 'package:supplier_new/resources/tanker_details.dart';
import 'package:supplier_new/resources/tankers_model.dart';
import 'fleet.dart';
import 'resources_drivers.dart';
@ -568,13 +569,23 @@ class _ResourcesFleetScreenState extends State<ResourcesFleetScreen> {
separatorBuilder: (_, __) => const SizedBox(height: 10),
itemBuilder: (context, idx) {
final it = filtered[idx];
return TankCard(
title: it.tanker_name,
subtitle: it.type_of_water,
capacity: it.capacity,
code: it.license_plate,
owner: it.supplier_name,
status: List<String>.from(it.availability),
return GestureDetector(
onTap: (){
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TankerDetailsPage(tankerDetails: it),
),
);
},
child: TankCard(
title: it.tanker_name,
subtitle: it.type_of_water,
capacity: it.capacity,
code: it.license_plate,
owner: it.supplier_name,
status: List<String>.from(it.availability),
),
);
},
),

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:supplier_new/common/settings.dart';
import 'package:supplier_new/resources/source_loctaions_model.dart';
import 'package:supplier_new/resources/sourcelocation_details.dart';
import '../google_maps_place_picker_mb/src/models/pick_result.dart';
import '../google_maps_place_picker_mb/src/place_picker.dart';
import 'package:supplier_new/google_maps_place_picker_mb/google_maps_place_picker.dart';
@ -600,9 +601,19 @@ class _ResourcesSourceScreenState extends State<ResourcesSourceScreen> {
separatorBuilder: (_, __) => const SizedBox(height: 10),
itemBuilder: (context, idx) {
final it = filtered[idx];
return TankCard(
title: it.source_name,
subtitle: it.address,
return GestureDetector(
onTap: (){
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SourceDetailsScreen(sourceDetails: it),
),
);
},
child: TankCard(
title: it.source_name,
subtitle: it.address,
),
);
},
),

@ -0,0 +1,237 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import '../common/settings.dart';
class SourceDetailsScreen extends StatefulWidget {
var sourceDetails;
var status;
SourceDetailsScreen({this.sourceDetails, this.status});
@override
State<SourceDetailsScreen> createState() => _SourceDetailsScreenState();
}
class _SourceDetailsScreenState extends State<SourceDetailsScreen> {
final List<Map<String, dynamic>> recentTrips = [
{"type": "Drinking Water", "liters": "10,000 L", "time": "7:02 PM", "date": "28 Jun 2025"},
{"type": "Drinking Water", "liters": "10,000 L", "time": "7:02 PM", "date": "28 Jun 2025"},
{"type": "Drinking Water", "liters": "10,000 L", "time": "7:02 PM", "date": "28 Jun 2025"},
];
void _showMenu() {
showModalBottomSheet(
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
builder: (context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.edit, color: Colors.black),
title: const Text('Edit'),
onTap: () => Navigator.pop(context),
),
ListTile(
leading: const Icon(Icons.block, color: Colors.black),
title: const Text('Disable'),
onTap: () => Navigator.pop(context),
),
ListTile(
leading: const Icon(Icons.delete_outline, color: Colors.red),
title: const Text('Delete', style: TextStyle(color: Colors.red)),
onTap: () => Navigator.pop(context),
),
],
),
);
},
);
}
@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_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)),
),
],
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Image
Stack(
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,
),
),
Positioned(
top: 8,
right: 8,
child: IconButton(
icon: const Icon(Icons.more_vert, color: Colors.white),
onPressed: _showMenu,
),
)
],
),
const SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(widget.sourceDetails.source_name,
style: fontTextStyle(18, Colors.black, FontWeight.w600)),
const SizedBox(height: 4),
Text("Drinking Water",
style: fontTextStyle(14, const Color(0xFF8270DB), FontWeight.w500)),
const SizedBox(height: 4),
Text("+91 789456212",
style: fontTextStyle(14, Colors.black, FontWeight.w400)),
const SizedBox(height: 4),
Text(
"Road No. 12, Krishnadwar Layout,\nGandipet, Hyderabad, 500065",
style: fontTextStyle(13, const Color(0xFF656565), FontWeight.w400),
),
],
),
),
const SizedBox(height: 20),
// Filling & Wait time cards
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Expanded(
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFFF7F7F7),
borderRadius: BorderRadius.circular(12),
),
child: Column(
children: [
Text("12",
style: fontTextStyle(20, Colors.black, FontWeight.w600)),
Text("sec/L",
style: fontTextStyle(14, Colors.black, FontWeight.w400)),
const SizedBox(height: 4),
Text("Filling Time",
style: fontTextStyle(12, Colors.grey, FontWeight.w400)),
],
),
),
),
const SizedBox(width: 12),
Expanded(
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFFF7F7F7),
borderRadius: BorderRadius.circular(12),
),
child: Column(
children: [
Text("24",
style: fontTextStyle(20, Colors.black, FontWeight.w600)),
Text("min",
style: fontTextStyle(14, Colors.black, FontWeight.w400)),
const SizedBox(height: 4),
Text("Wait Time",
style: fontTextStyle(12, Colors.grey, FontWeight.w400)),
],
),
),
),
],
),
),
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text("Recent Trips",
style: fontTextStyle(16, Colors.black, FontWeight.w600)),
),
const SizedBox(height: 8),
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: recentTrips.length,
itemBuilder: (context, index) {
final trip = recentTrips[index];
return Padding(
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFFF7F7F7),
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
const Icon(Icons.water_drop, color: Color(0xFF8270DB)),
const SizedBox(width: 12),
Expanded(
child: Text(
"${trip['type']} - ${trip['liters']}",
style: fontTextStyle(
14, const Color(0xFF2D2E30), FontWeight.w500),
),
),
Text(
"${trip['time']}, ${trip['date']}",
style: fontTextStyle(
12, const Color(0xFF656565), FontWeight.w400),
),
],
),
),
);
},
),
const SizedBox(height: 20),
],
),
),
);
}
}

@ -0,0 +1,281 @@
import 'package:flutter/material.dart';
class TankerDetailsPage extends StatefulWidget {
var tankerDetails;
var status;
TankerDetailsPage({this.tankerDetails, this.status});
@override
State<TankerDetailsPage> createState() => _TankerDetailsPageState();
}
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),
),
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,
),
),
centerTitle: false,
actions: [
TextButton(
onPressed: () {},
child: const Text(
"HELP",
style: TextStyle(
color: Color(0xFF4F46E5),
fontWeight: FontWeight.w600,
fontSize: 14,
),
),
),
],
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 🛻 Tanker Image
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Stack(
children: [
Image.asset(
'images/tanker_image.jpeg', // Replace with your image
width: double.infinity,
height: 180,
fit: BoxFit.cover,
),
/*Positioned(
bottom: 12,
left: 12,
child: Row(
children: [
_buildStatusChip("filled", const Color(0xFF4F46E5)),
const SizedBox(width: 8),
_buildStatusChip("available", const Color(0xFF0A9E04)),
],
),
),*/
],
),
),
const SizedBox(height: 16),
Row(
children: [
_buildStatusChip("filled", const Color(0xFF4F46E5)),
const SizedBox(width: 8),
_buildStatusChip("available", const Color(0xFF0A9E04)),
],
),
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,
),
),
SizedBox(height: 2),
Text(
widget.tankerDetails.tanker_name,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: Colors.black,
),
),
SizedBox(height: 2),
Text(
widget.tankerDetails.type_of_water+' - '+widget.tankerDetails.capacity+' L',
style: TextStyle(
fontSize: 12,
color: Colors.black54,
),
),
],
),
),
Text(
widget.tankerDetails.license_plate,
style: TextStyle(
fontSize: 12,
color: Colors.black54,
fontWeight: FontWeight.w500,
),
),
],
),
const SizedBox(height: 24),
// 📍 Recent Trips
const Text(
"RECENT TRIPS",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
const SizedBox(height: 12),
_buildTripCard(
driverName: "Ramesh Krishna",
time: "7:02 PM, 28 Jun 2025",
from: "Bachupally Filling Station",
to: "Akriti Heights",
),
const SizedBox(height: 8),
_buildTripCard(
driverName: "Ramesh Krishna",
time: "12:44 PM, 26 Jun 2025",
from: "Bachupally Filling Station",
to: "Akriti Heights",
),
const SizedBox(height: 30),
],
),
),
),
);
}
Widget _buildStatusChip(String label, Color color) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: color),
),
child: Text(
label,
style: TextStyle(
color: color,
fontSize: 12,
fontWeight: FontWeight.w600,
),
),
);
}
Widget _buildTripCard({
required String driverName,
required String time,
required String from,
required String to,
}) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFFF8F8F8),
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Driver + time
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
const CircleAvatar(
radius: 14,
backgroundImage: AssetImage('images/avatar.png'),
),
const SizedBox(width: 8),
Text(
driverName,
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 13,
),
),
],
),
Text(
time,
style: const TextStyle(
fontSize: 11,
color: Colors.black54,
),
),
],
),
const SizedBox(height: 8),
// From location
Row(
children: [
const Icon(Icons.location_on, color: Color(0xFF4F46E5), size: 16),
const SizedBox(width: 6),
Expanded(
child: Text(
from,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
),
],
),
const SizedBox(height: 4),
// To location
Row(
children: [
const Icon(Icons.location_on, color: Colors.red, size: 16),
const SizedBox(width: 6),
Expanded(
child: Text(
to,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
),
],
),
],
),
);
}
}

@ -3,6 +3,8 @@ class TankersModel {
String address = '';
String type_of_water = '';
String capacity = '';
String price = '';
String delivery_fee = '';
String dbId = '';
String status='';
String license_plate='';
@ -20,6 +22,8 @@ class TankersModel {
rtvm.capacity = json['capacity'] ?? '';
rtvm.license_plate = json['license_plate'] ?? '';
rtvm.supplier_name = json['supplier_name'] ?? '';
rtvm.price = json['price'] ?? '';
rtvm.delivery_fee = json['delivery_fee'] ?? '';
return rtvm;
}

@ -1,6 +1,10 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:supplier_new/common/settings.dart';
import '../resources/tankers_model.dart';
class SetRatesScreen extends StatefulWidget {
const SetRatesScreen({super.key});
@ -16,52 +20,95 @@ class _SetRatesScreenState extends State<SetRatesScreen>
// Define categories + tankers
final Map<String, List<String>> tankerGroups = {};
final Map<String, TextEditingController> deliveryControllers = {};
/*delivery fee controllers*/
final TextEditingController pumpFeeController = TextEditingController();
List<TankersModel> tankersList = [];
bool isLoading = false;
final List<Map<String, dynamic>> data = [
{"category": "DRINKING WATER", "size": "5,000L", "price": "12"},
{"category": "DRINKING WATER", "size": "15,000L", "price": "18"},
{"category": "DRINKING WATER", "size": "20,000L", "price": "20"},
{"category": "BORE WATER", "size": "5,000L", "price": "10"},
{"category": "BORE WATER", "size": "10,000L", "price": "14"},
{"category": "BORE WATER", "size": "15,000L", "price": "16"},
{"category": "BORE WATER", "size": "25,000L", "price": null},
];
Future<void> _fetchTankers() async {
setState(() => isLoading = true);
@override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
try {
final response = await AppSettings.getTankers();
final data = (jsonDecode(response)['data'] as List)
.map((e) => TankersModel.fromJson(e))
.toList();
if (!mounted) return;
setState(() {
// 🧹 Clear old data before adding new
tankersList.clear();
tankerGroups.clear();
controllers.clear();
deliveryControllers.clear();
// Group data by category and initialize controllers for WaterCharges
for (final item in data) {
final category = item['category'] as String;
final size = item['size'] as String;
final price = item['price']?.toString() ?? ""; // default to empty if null
// 🆕 Assign new data
tankersList = data;
isLoading = false;
if (!tankerGroups.containsKey(category)) {
tankerGroups[category] = [];
}
tankerGroups[category]!.add(size);
// 🧭 Group data and create controllers
for (final item in data) {
final category = item.type_of_water;
final size = item.capacity;
final price = item.price.toString();
// Pre-fill controller with price or empty
controllers.putIfAbsent("$category-$size", () => TextEditingController(text: price));
if (!tankerGroups.containsKey(category)) {
tankerGroups[category] = [];
}
// avoid duplicate sizes for same category
if (!tankerGroups[category]!.contains(size)) {
tankerGroups[category]!.add(size);
}
final controllerKey = "$category-$size";
controllers[controllerKey] = TextEditingController(text: price);
}
final capacities = tankersList.map((t) => normalizeCap(t.capacity)).toSet();
for (final cap in capacities) {
// take the first tanker of this capacity to prefill (optional)
final matched = tankersList.firstWhere(
(t) => normalizeCap(t.capacity) == cap,
orElse: () => TankersModel(),
);
final deliveryFee = matched.delivery_fee.toString() ?? "";
deliveryControllers.putIfAbsent(
cap,
() => TextEditingController(text: deliveryFee),
);
}
});
} catch (e) {
debugPrint("⚠️ Error fetching tankers: $e");
setState(() => isLoading = false);
}
}
Future<void> _fetchPumpFee() async {
try {
final response = await AppSettings.getSupplierDetails();
final data = jsonDecode(response);
if (!mounted) return;
setState(() {
pumpFeeController.text=data['pumping_fee'];
});
} catch (e) {
debugPrint("⚠️ Error fetching tankers: $e");
// Initialize controllers for unique capacities for DeliveryFee
final capacities = data.map((item) => item['size'] as String).toSet();
for (final cap in capacities) {
// Take first price for this capacity, default to empty if null
final price = data.firstWhere(
(d) => d['size'] == cap,
orElse: () => {'price': ''},
)['price']?.toString() ?? "";
deliveryControllers.putIfAbsent(cap, () => TextEditingController(text: price));
}
}
@override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
_fetchTankers();
_fetchPumpFee();
}
@override
@ -126,24 +173,35 @@ class _SetRatesScreenState extends State<SetRatesScreen>
}
Widget WaterCharges() {
return SingleChildScrollView(
return isLoading
? const Center(child: CircularProgressIndicator()):SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
child: tankersList.isEmpty?Center(
child: Text(
'No Data Available',
style: fontTextStyle(16,Color(0XFF000000),FontWeight.w700),
),
)
:Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (final entry in tankerGroups.entries) ...[
Text(
entry.key,
style: fontTextStyle(10, Color(0XFF2D2E30), FontWeight.w600),
style:
fontTextStyle(10, const Color(0XFF2D2E30), FontWeight.w600),
),
const SizedBox(height: 8),
for (final size in entry.value)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
labelText("$size (in ₹)*"),
labelText("$size L (in ₹)*"),
const SizedBox(height: 4),
buildTextField("₹500", controllers["${entry.key}-$size"]!),
buildTextField(
"₹500",
controllers["${entry.key}-$size"]!,
),
const SizedBox(height: 12),
],
),
@ -153,32 +211,63 @@ class _SetRatesScreenState extends State<SetRatesScreen>
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Color(0XFF8270DB),
foregroundColor: Color(0XFFFFFFFF),
padding: EdgeInsets.symmetric(vertical: 10),
backgroundColor: const Color(0XFF8270DB),
foregroundColor: const Color(0XFFFFFFFF),
padding: const EdgeInsets.symmetric(vertical: 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24), // <-- set your radius here
borderRadius: BorderRadius.circular(24),
),
),
onPressed: () {
final Map<String, Map<String, String>> grouped = {};
onPressed: () async {
final List<Map<String, String>> tankersPayload = [];
tankerGroups.forEach((category, sizes) {
grouped[category] = {};
for (final size in sizes) {
grouped[category]![size] =
controllers["$category-$size"]!.text;
final key = "$category-$size";
final amount = controllers[key]?.text.trim() ?? "0";
// find the tanker in tankersList by capacity (size)
final matchedTanker = tankersList.firstWhere(
(t) => t.capacity == size,
orElse: () => TankersModel(),
);
final tankerName = matchedTanker.tanker_name.isNotEmpty
? matchedTanker.tanker_name
: "$category $size L"; // fallback if not found
tankersPayload.add({
"tankerName": tankerName,
"amount": amount.isEmpty ? "0" : amount,
});
}
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Rates saved successfully")),
);
final payload = {
"price_type": "price",
"tankers": tankersPayload,
};
print(payload);
print(grouped); // Debug: print entered rates
try {
final ok = await AppSettings.setRatesDaily(payload);
if (ok) {
AppSettings.longSuccessToast("Prices updated successfully");
_fetchTankers();
} else {
AppSettings.longFailedToast("Update failed");
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Error: $e")),
);
}
},
child: Text(
"Save",
style: fontTextStyle(14, const Color(0XFFFFFFFF), FontWeight.w600),
style:
fontTextStyle(14, const Color(0XFFFFFFFF), FontWeight.w600),
),
),
)
@ -187,17 +276,32 @@ class _SetRatesScreenState extends State<SetRatesScreen>
);
}
String normalizeCap(String cap) => cap.replaceAll(',', '').replaceAll(' ', '');
Widget DeliveryFee() {
// Extract unique capacities
final capacities = data.map((item) => item['size'] as String).toSet().toList();
// Group by normalized capacity
final Map<String, List<TankersModel>> capacityGroups = {};
for (final t in tankersList) {
final normCap = normalizeCap(t.capacity);
capacityGroups.putIfAbsent(normCap, () => []).add(t);
}
// Sorted list by numeric capacity (if parseable), else lexicographic
final capacities = capacityGroups.keys.toList()
..sort((a, b) {
final ai = int.tryParse(a);
final bi = int.tryParse(b);
if (ai != null && bi != null) return ai.compareTo(bi);
return a.compareTo(b);
});
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
Text(
"ADDITIONAL RATES",
style: fontTextStyle(10, const Color(0xFF2D2E30), FontWeight.w600),
),
@ -208,11 +312,16 @@ class _SetRatesScreenState extends State<SetRatesScreen>
),
SizedBox(height: MediaQuery.of(context).size.height * .020),
// 🔹 Dynamic textfields
for (final cap in capacities) ...[
labelText('$cap tanker (per KM)*'),
// one field per normalized capacity
for (final normCap in capacities) ...[
// show without commas (normalized already)
labelText('${normCap} L tanker (per KM)*'),
SizedBox(height: MediaQuery.of(context).size.height * .004),
buildTextField("+ ₹12", deliveryControllers[cap]!),
buildTextField(
"+ ₹12",
// controller keyed by normalized cap
deliveryControllers[normCap] ?? TextEditingController(),
),
const SizedBox(height: 12),
],
@ -221,37 +330,66 @@ class _SetRatesScreenState extends State<SetRatesScreen>
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Color(0XFF8270DB),
foregroundColor: Color(0XFFFFFFFF),
padding: EdgeInsets.symmetric(vertical: 10),
backgroundColor: const Color(0XFF8270DB),
foregroundColor: const Color(0XFFFFFFFF),
padding: const EdgeInsets.symmetric(vertical: 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24), // <-- set your radius here
borderRadius: BorderRadius.circular(24),
),
),
onPressed: () {
final Map<String, String> fees = {};
deliveryControllers.forEach((cap, controller) {
fees[cap] = controller.text;
onPressed: () async {
final List<Map<String, String>> tankersPayload = [];
// loop groups; use the same fee for all tankers in the group
capacityGroups.forEach((normCap, list) {
final fee = (deliveryControllers[normCap]?.text ?? "").trim();
final amt = fee.isEmpty ? "0" : fee;
for (final t in list) {
final tankerName = t.tanker_name.isNotEmpty
? t.tanker_name
: "Tanker ${normalizeCap(t.capacity)} L";
tankersPayload.add({
"tankerName": tankerName,
"amount": amt,
});
}
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Rates saved successfully")),
);
final payload = {
"price_type": "delivery_fee",
"tankers": tankersPayload,
};
print(payload);
print(fees);
try {
final ok = await AppSettings.setRatesDaily(payload);
if (ok) {
AppSettings.longSuccessToast("Delivery fees updated successfully");
_fetchTankers();
} else {
AppSettings.longFailedToast("Update failed");
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Error: $e")),
);
}
},
child: Text(
"Save",
style: fontTextStyle(14, const Color(0XFFFFFFFF), FontWeight.w600),
),
),
)
),
],
),
);
}
Widget PumpFee() {
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
@ -278,18 +416,32 @@ class _SetRatesScreenState extends State<SetRatesScreen>
foregroundColor: Color(0XFFFFFFFF),
padding: EdgeInsets.symmetric(vertical: 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24), // <-- set your radius here
borderRadius:
BorderRadius.circular(24), // <-- set your radius here
),
),
onPressed: () {
// Handle save logic
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Rates saved successfully")),
);
onPressed: () async{
var payload = new Map<String, dynamic>();
payload["pumping_fee"] = pumpFeeController.text.toString();
bool tankStatus = await AppSettings.updatePumpFee(payload);
try {
if (tankStatus) {
AppSettings.longSuccessToast("Pump fee Updated Successfully");
_fetchPumpFee();
}
else {
AppSettings.longFailedToast("Pump fee updation failed");
}
} catch (exception) {
print(exception);
}
},
child: Text(
"Save",
style: fontTextStyle(14, const Color(0XFFFFFFFF), FontWeight.w600),
style:
fontTextStyle(14, const Color(0XFFFFFFFF), FontWeight.w600),
),
),
)
@ -317,9 +469,10 @@ class _SetRatesScreenState extends State<SetRatesScreen>
child: Column(
children: [
SizedBox(height: MediaQuery.of(context).size.height * .096),
const CircleAvatar(radius: 50, backgroundColor: Color(0XFFC9C2F0)),
const CircleAvatar(
radius: 50, backgroundColor: Color(0XFFC9C2F0)),
SizedBox(height: MediaQuery.of(context).size.height * .016),
Text(
Text(
"Whats todays water price?",
style: fontTextStyle(20, Color(0XFF343637), FontWeight.w600),
),
@ -344,28 +497,33 @@ class _SetRatesScreenState extends State<SetRatesScreen>
indicatorColor: Colors.transparent, // remove underline
dividerColor: Colors.transparent,
isScrollable: false,
overlayColor: MaterialStateProperty.all(Colors.transparent),// equal width
overlayColor: MaterialStateProperty.all(
Colors.transparent), // equal width
tabs: List.generate(3, (index) {
final labels = ['Water Type', 'Delivery Fee','Pump'];
final labels = ['Water Type', 'Delivery Fee', 'Pump'];
final isSelected = _tabController.index == index;
return Container(
decoration: BoxDecoration(
color: isSelected ? const Color(0XFFF1F1F1) : Colors.transparent,
color: isSelected
? const Color(0XFFF1F1F1)
: Colors.transparent,
borderRadius: BorderRadius.circular(27),
),
alignment: Alignment.center,
child: Text(
labels[index],
style: isSelected ?fontTextStyle(
12,
const Color(0XFF101214),
FontWeight.w600,
):fontTextStyle(
12,
const Color(0XFF646464),
FontWeight.w600,
),
style: isSelected
? fontTextStyle(
12,
const Color(0XFF101214),
FontWeight.w600,
)
: fontTextStyle(
12,
const Color(0XFF646464),
FontWeight.w600,
),
),
);
}),

Loading…
Cancel
Save