parent
23e9425217
commit
20fcf1e9af
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
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),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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,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),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Loading…
Reference in new issue