master
Sneha 5 days ago
parent 92e704a1b4
commit ce55bf125b

@ -197,6 +197,8 @@ class AppSettings{
static String acceptRequestUrl = host +"friend-request/accept";
static String rejectRequestUrl = host +"friend-request/reject";
static String getDriverTripsUrl = host +"getdeliveryboybookings";
static String verifyUnloadStartOTPUrl = host + 'deliveryboystartandstop';
static String amountpaidByCustomerUrl = host + 'amountpaidByCustomer';
static int driverAvailableCount = 0;
@ -1820,6 +1822,50 @@ class AppSettings{
}
static Future<String> verifyUnloadStartOTP(String bookingId, payload) async {
final uri = Uri.parse(verifyUnloadStartOTPUrl+'/'+bookingId);
var response = await http.put(uri, body: jsonEncode(payload), headers: await buildRequestHeaders());
if (response.statusCode == 200) {
return response.body;
} else if (response.statusCode == 401) {
bool status = await AppSettings.resetToken();
if (status) {
response = await await http.put(uri, body: jsonEncode(payload), headers: await buildRequestHeaders());
if (response.statusCode == 200) {
return response.body;
} else {
return '';
}
} else {
return '';
}
} else {
return '';
}
}
static Future<String> amountpaidByCustomer(String bookingId, payload) async {
final uri = Uri.parse(amountpaidByCustomerUrl+'/'+bookingId);
var response = await http.put(uri, body: jsonEncode(payload), headers: await buildRequestHeaders());
if (response.statusCode == 200) {
return response.body;
} else if (response.statusCode == 401) {
bool status = await AppSettings.resetToken();
if (status) {
response = await await http.put(uri, body: jsonEncode(payload), headers: await buildRequestHeaders());
if (response.statusCode == 200) {
return response.body;
} else {
return '';
}
} else {
return '';
}
} else {
return '';
}
}
/*Apis ends here*/
//save data local
@ -2170,4 +2216,61 @@ class AppSettings{
);
}
static appBarWithNotificationIcon(String buildingName,String typeOfWater,String capacity,context){
buildingName = buildingName ?? '';
typeOfWater = typeOfWater ?? '';
capacity = capacity ?? '';
return AppBar(
backgroundColor: Colors.white,
elevation: 0,
scrolledUnderElevation: 0,
titleSpacing: 0,
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(buildingName,
style: fontTextStyle(16, const Color(0XFF2A2A2A), FontWeight.w600)),
const SizedBox(height: 2),
Text('$typeOfWater | $capacity',
style: fontTextStyle(12, const Color(0XFF2A2A2A), FontWeight.w400)),
],
),
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,
),
),
),
actions: [
Row(
children: [
Padding(
padding: EdgeInsets.fromLTRB(0, 10, 10, 10),
child: IconButton(
icon: Image.asset(
'images/notification_appbar.png',
height: 20,
width: 20,// Example URL image
),
onPressed: () {},
),
)
],
)
],
);
}
}

@ -1265,6 +1265,7 @@ class _OrderCardState extends State<OrderCard>{
MaterialPageRoute(
builder:(context)=>
DeliveryUpdatesPage(
details:widget.order,
orderId:widget.order.dbId,
initialStatus:widget.order.status
)

@ -8,6 +8,8 @@ import '../resources/tankers_model.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'assign_selection_screen.dart';
class AssignDriverScreen extends StatefulWidget {
var order;
var status;
@ -1425,6 +1427,26 @@ class _AssignDriverScreenState extends State<AssignDriverScreen> {
onPressed: () async {
_showAssignTankerBottomSheet();
},
/* onPressed: () async {
bool? result = await Navigator.push(
cont
ext,
MaterialPageRoute(
builder: (_) => AssignTankerScreen(
order: widget.order,
driversList: driversList,
tankersList: tankersList,
sourceLocationsList: sourceLocationsList,
),
),
);
if(result == true){
Navigator.pop(context,true);
}
},*/
child: Text(
"Assign Tanker",
style: fontTextStyle(
@ -1437,7 +1459,527 @@ class _AssignDriverScreenState extends State<AssignDriverScreen> {
);
}
/// 🔹 Helper widget for rows
/// 🔹 Helper widget for rowsimport 'dart:convert';
// import 'package:flutter/material.dart';
// import 'package:intl/intl.dart';
// import 'package:supplier_new/common/settings.dart';
// import '../resources/drivers_model.dart';
// import '../resources/tankers_model.dart';
// import '../resources/source_loctaions_model.dart';
//
// class AssignTankerScreen extends StatefulWidget {
//
// final order;
// final List<DriversModel> driversList;
// final List<TankersModel> tankersList;
// final List<SourceLocationsModel> sourceLocationsList;
//
// AssignTankerScreen({
// this.order,
// required this.driversList,
// required this.tankersList,
// required this.sourceLocationsList,
// });
//
// @override
// State<AssignTankerScreen> createState() =>
// _AssignTankerScreenState();
// }
//
// class _AssignTankerScreenState
// extends State<AssignTankerScreen> {
//
// int? selectedTankerIndex;
// int? selectedDriverIndex;
// int? selectedSourceIndex;
//
// int _capToLiters(dynamic cap){
//
// if(cap==null)return -1;
//
// if(cap is num){
// return cap.round();
// }
//
// final s=cap
// .toString()
// .toLowerCase()
// .replaceAll(',','')
// .trim();
//
// final match=
// RegExp(r'(\d+(\.\d+)?)')
// .firstMatch(s);
//
// if(match==null)return -1;
//
// final n=
// double.tryParse(match.group(1)!)??-1;
//
// if(n<0)return -1;
//
// if(s.contains('kl')){
// return (n*1000).round();
// }
//
// return n.round();
//
// }
//
// DateTime parseOrderDateTime(){
//
// DateTime d=
// DateFormat("dd-MMM-yyyy")
// .parse(widget.order.date);
//
// DateTime t=
// DateFormat("hh:mm a")
// .parse(widget.order.time);
//
// return DateTime(
// d.year,
// d.month,
// d.day,
// t.hour,
// t.minute
// );
//
// }
//
// bool isTankerBlocked(
// TankersModel tanker){
//
// if(tanker.blocked_dates==null ||
// tanker.blocked_dates.isEmpty){
// return false;
// }
//
// DateTime orderDT=
// parseOrderDateTime();
//
// for(var slot
// in tanker.blocked_dates){
//
// if(slot['date']!=
// widget.order.date){
// continue;
// }
//
// String timeRange=
// slot['time'];
//
// List parts=
// timeRange.split("to");
//
// if(parts.length!=2){
// continue;
// }
//
// DateTime start=
// DateFormat("hh:mm a")
// .parse(parts[0].trim());
//
// DateTime end=
// DateFormat("hh:mm a")
// .parse(parts[1].trim());
//
// DateTime startDT=
// DateTime(
// orderDT.year,
// orderDT.month,
// orderDT.day,
// start.hour,
// start.minute
// );
//
// DateTime endDT=
// DateTime(
// orderDT.year,
// orderDT.month,
// orderDT.day,
// end.hour,
// end.minute
// );
//
// if(orderDT.isAfter(startDT)
// &&
// orderDT.isBefore(endDT)){
// return true;
// }
//
// if(orderDT==startDT ||
// orderDT==endDT){
// return true;
// }
//
// }
//
// return false;
//
// }
//
// String? getDriverBlockedTime(
// DriversModel driver){
//
// if(driver.blocked_dates==null ||
// driver.blocked_dates.isEmpty){
// return null;
// }
//
// DateTime orderDT=
// parseOrderDateTime();
//
// for(var slot
// in driver.blocked_dates){
//
// if(slot['date']!=
// widget.order.date){
// continue;
// }
//
// String timeRange=
// slot['time'];
//
// List parts=
// timeRange.split("to");
//
// if(parts.length!=2){
// continue;
// }
//
// DateTime start=
// DateFormat("hh:mm a")
// .parse(parts[0].trim());
//
// DateTime end=
// DateFormat("hh:mm a")
// .parse(parts[1].trim());
//
// DateTime startDT=
// DateTime(
// orderDT.year,
// orderDT.month,
// orderDT.day,
// start.hour,
// start.minute
// );
//
// DateTime endDT=
// DateTime(
// orderDT.year,
// orderDT.month,
// orderDT.day,
// end.hour,
// end.minute
// );
//
// if(orderDT.isAfter(startDT)
// &&
// orderDT.isBefore(endDT)){
// return timeRange;
// }
//
// if(orderDT==startDT ||
// orderDT==endDT){
// return timeRange;
// }
//
// }
//
// return null;
//
// }
//
// @override
// Widget build(BuildContext context){
//
// return Scaffold(
//
// backgroundColor: Colors.white,
//
// appBar: AppBar(
//
// title:
// Text("Assign Tanker"),
//
// ),
//
// body:
//
// Column(
//
// children:[
//
// Expanded(
//
// child:
//
// SingleChildScrollView(
//
// padding:
// EdgeInsets.all(16),
//
// child:
//
// Column(
//
// crossAxisAlignment:
// CrossAxisAlignment.start,
//
// children:[
//
// /// ORDER CARD
//
// Container(
//
// padding:
// EdgeInsets.all(12),
//
// decoration:
// BoxDecoration(
//
// borderRadius:
// BorderRadius.circular(12),
//
// border:
// Border.all(
// color:
// Color(0XFFC9C2F0)
// ),
//
// ),
//
// child:
//
// Row(
//
// children:[
//
// Column(
//
// crossAxisAlignment:
// CrossAxisAlignment.start,
//
// children:[
//
// Text(
// widget.order.building_name
// ),
//
// Text(
// widget.order.displayAddress
// ),
//
// ],
//
// ),
//
// Spacer(),
//
// Text(
// "${widget.order.distanceInKm} Km"
// ),
//
// ],
//
// ),
//
// ),
//
// SizedBox(height:20),
//
// /// TANKERS
//
// Text("SELECT TANKER"),
//
// SizedBox(height:10),
//
// ...widget.tankersList
// .where((t)=>
//
// _capToLiters(t.capacity)==
// _capToLiters(widget.order.capacity)
//
// ).toList()
// .asMap()
// .entries
// .map((entry){
//
// int idx=
// entry.key;
//
// var d=
// entry.value;
//
// bool blocked=
// isTankerBlocked(d);
//
// return GestureDetector(
//
// onTap:blocked?null:(){
//
// setState(() {
//
// selectedTankerIndex=
// idx;
//
// selectedDriverIndex=
// null;
//
// });
//
// },
//
// child:
//
// Card(
//
// child:
//
// Padding(
//
// padding:
// EdgeInsets.all(12),
//
// child:
//
// Text(
// d.tanker_name
// ),
//
// ),
//
// ),
//
// );
//
// }).toList(),
//
// ],
//
// ),
//
// ),
//
// ),
//
// /// ASSIGN BUTTON
//
// Padding(
//
// padding:
// EdgeInsets.all(16),
//
// child:
//
// SizedBox(
//
// width:double.infinity,
//
// child:
//
// ElevatedButton(
//
// style:
// ElevatedButton.styleFrom(
//
// backgroundColor:
// Color(0XFF8270DB)
//
// ),
//
// onPressed:()async{
//
// if(selectedTankerIndex==null){
//
// AppSettings
// .longFailedToast(
// "Select tanker"
// );
//
// return;
//
// }
//
// final selectedTanker=
// widget.tankersList[
// selectedTankerIndex!
// ];
//
// final selectedDriver=
// selectedDriverIndex!=null
// ?
// widget.driversList[
// selectedDriverIndex!
// ]
// :
// null;
//
// final selectedSource=
// selectedSourceIndex!=null
// ?
// widget.sourceLocationsList[
// selectedSourceIndex!
// ]
// :
// null;
//
// var payload={};
//
// payload["tankerName"]=
// selectedTanker.tanker_name;
//
// payload["delivery_agent"]=
// selectedDriver?.driver_name;
//
// payload["delivery_agent_mobile"]=
// selectedDriver?.phone_number;
//
// payload["water_source_location"]=
// selectedSource?.source_name;
//
// bool status=
// await AppSettings
// .assignTanker(
// payload,
// widget.order.dbId
// );
//
// if(status){
//
// AppSettings
// .longSuccessToast(
// "Assigned"
// );
//
// Navigator.pop(
// context,
// true
// );
//
// }
//
// },
//
// child:
//
// Text("Assign"),
//
// ),
//
// ),
//
// )
//
// ],
//
// ),
//
// );
//
// }
//
// }
Widget _detailRow(String title, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 0),

@ -0,0 +1,521 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:supplier_new/common/settings.dart';
import '../resources/drivers_model.dart';
import '../resources/tankers_model.dart';
import '../resources/source_loctaions_model.dart';
class AssignTankerScreen extends StatefulWidget {
final order;
final List<DriversModel> driversList;
final List<TankersModel> tankersList;
final List<SourceLocationsModel> sourceLocationsList;
AssignTankerScreen({
this.order,
required this.driversList,
required this.tankersList,
required this.sourceLocationsList,
});
@override
State<AssignTankerScreen> createState() =>
_AssignTankerScreenState();
}
class _AssignTankerScreenState
extends State<AssignTankerScreen> {
int? selectedTankerIndex;
int? selectedDriverIndex;
int? selectedSourceIndex;
int _capToLiters(dynamic cap){
if(cap==null)return -1;
if(cap is num){
return cap.round();
}
final s=cap
.toString()
.toLowerCase()
.replaceAll(',','')
.trim();
final match=
RegExp(r'(\d+(\.\d+)?)')
.firstMatch(s);
if(match==null)return -1;
final n=
double.tryParse(match.group(1)!)??-1;
if(n<0)return -1;
if(s.contains('kl')){
return (n*1000).round();
}
return n.round();
}
DateTime parseOrderDateTime(){
DateTime d=
DateFormat("dd-MMM-yyyy")
.parse(widget.order.date);
DateTime t=
DateFormat("hh:mm a")
.parse(widget.order.time);
return DateTime(
d.year,
d.month,
d.day,
t.hour,
t.minute
);
}
bool isTankerBlocked(
TankersModel tanker){
if(tanker.blocked_dates==null ||
tanker.blocked_dates.isEmpty){
return false;
}
DateTime orderDT=
parseOrderDateTime();
for(var slot
in tanker.blocked_dates){
if(slot['date']!=
widget.order.date){
continue;
}
String timeRange=
slot['time'];
List parts=
timeRange.split("to");
if(parts.length!=2){
continue;
}
DateTime start=
DateFormat("hh:mm a")
.parse(parts[0].trim());
DateTime end=
DateFormat("hh:mm a")
.parse(parts[1].trim());
DateTime startDT=
DateTime(
orderDT.year,
orderDT.month,
orderDT.day,
start.hour,
start.minute
);
DateTime endDT=
DateTime(
orderDT.year,
orderDT.month,
orderDT.day,
end.hour,
end.minute
);
if(orderDT.isAfter(startDT)
&&
orderDT.isBefore(endDT)){
return true;
}
if(orderDT==startDT ||
orderDT==endDT){
return true;
}
}
return false;
}
String? getDriverBlockedTime(
DriversModel driver){
if(driver.blocked_dates==null ||
driver.blocked_dates.isEmpty){
return null;
}
DateTime orderDT=
parseOrderDateTime();
for(var slot
in driver.blocked_dates){
if(slot['date']!=
widget.order.date){
continue;
}
String timeRange=
slot['time'];
List parts=
timeRange.split("to");
if(parts.length!=2){
continue;
}
DateTime start=
DateFormat("hh:mm a")
.parse(parts[0].trim());
DateTime end=
DateFormat("hh:mm a")
.parse(parts[1].trim());
DateTime startDT=
DateTime(
orderDT.year,
orderDT.month,
orderDT.day,
start.hour,
start.minute
);
DateTime endDT=
DateTime(
orderDT.year,
orderDT.month,
orderDT.day,
end.hour,
end.minute
);
if(orderDT.isAfter(startDT)
&&
orderDT.isBefore(endDT)){
return timeRange;
}
if(orderDT==startDT ||
orderDT==endDT){
return timeRange;
}
}
return null;
}
@override
Widget build(BuildContext context){
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title:
Text("Assign Tanker"),
),
body:
Column(
children:[
Expanded(
child:
SingleChildScrollView(
padding:
EdgeInsets.all(16),
child:
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children:[
/// ORDER CARD
Container(
padding:
EdgeInsets.all(12),
decoration:
BoxDecoration(
borderRadius:
BorderRadius.circular(12),
border:
Border.all(
color:
Color(0XFFC9C2F0)
),
),
child:
Row(
children:[
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children:[
Text(
widget.order.building_name
),
Text(
widget.order.displayAddress
),
],
),
Spacer(),
Text(
"${widget.order.distanceInKm} Km"
),
],
),
),
SizedBox(height:20),
/// TANKERS
Text("SELECT TANKER"),
SizedBox(height:10),
...widget.tankersList
.where((t)=>
_capToLiters(t.capacity)==
_capToLiters(widget.order.capacity)
).toList()
.asMap()
.entries
.map((entry){
int idx=
entry.key;
var d=
entry.value;
bool blocked=
isTankerBlocked(d);
return GestureDetector(
onTap:blocked?null:(){
setState(() {
selectedTankerIndex=
idx;
selectedDriverIndex=
null;
});
},
child:
Card(
child:
Padding(
padding:
EdgeInsets.all(12),
child:
Text(
d.tanker_name
),
),
),
);
}).toList(),
],
),
),
),
/// ASSIGN BUTTON
Padding(
padding:
EdgeInsets.all(16),
child:
SizedBox(
width:double.infinity,
child:
ElevatedButton(
style:
ElevatedButton.styleFrom(
backgroundColor:
Color(0XFF8270DB)
),
onPressed:()async{
if(selectedTankerIndex==null){
AppSettings
.longFailedToast(
"Select tanker"
);
return;
}
final selectedTanker=
widget.tankersList[
selectedTankerIndex!
];
final selectedDriver=
selectedDriverIndex!=null
?
widget.driversList[
selectedDriverIndex!
]
:
null;
final selectedSource=
selectedSourceIndex!=null
?
widget.sourceLocationsList[
selectedSourceIndex!
]
:
null;
var payload={};
payload["tankerName"]=
selectedTanker.tanker_name;
payload["delivery_agent"]=
selectedDriver?.driver_name;
payload["delivery_agent_mobile"]=
selectedDriver?.phone_number;
payload["water_source_location"]=
selectedSource?.source_name;
bool status=
await AppSettings
.assignTanker(
payload,
widget.order.dbId
);
if(status){
AppSettings
.longSuccessToast(
"Assigned"
);
Navigator.pop(
context,
true
);
}
},
child:
Text("Assign"),
),
),
)
],
),
);
}
}

@ -0,0 +1,224 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:qr_flutter/qr_flutter.dart';
import '../common/settings.dart';
import 'all_orders.dart';
class CollectMoney extends StatefulWidget {
var details;
CollectMoney({this.details});
@override
State<CollectMoney> createState() => _CollectMoneyState();
}
class _CollectMoneyState extends State<CollectMoney> {
String collectAmountInRupees = '';
@override
void initState() {
super.initState();
// Safe parsing of amount_due (avoid NaN)
String raw = widget.details.amount_due?.toString() ?? "";
double? parsed = double.tryParse(raw);
if (parsed == null || parsed.isNaN) {
collectAmountInRupees = "0";
} else {
collectAmountInRupees = parsed.toStringAsFixed(0);
}
}
String formatDeliveredDate() {
final now = DateTime.now();
// Month names
const months = [
"Jan","Feb","Mar","Apr","May","Jun",
"Jul","Aug","Sep","Oct","Nov","Dec"
];
String day = now.day.toString().padLeft(2, '0');
String month = months[now.month - 1];
String year = now.year.toString();
int hour12 = now.hour > 12 ? now.hour - 12 : (now.hour == 0 ? 12 : now.hour);
String hour = hour12.toString();
String minute = now.minute.toString().padLeft(2, '0');
String ampm = now.hour >= 12 ? "PM" : "AM";
return "$day-$month-$year $hour:$minute $ampm";
}
@override
Widget build(BuildContext context) {
// --------------------------------------------
// 🔥 UPI QR GENERATION
// --------------------------------------------
String upiId = widget.details.supplier_upi_id ?? "";
String supplierName =
Uri.encodeComponent(widget.details.supplier_name ?? "Supplier");
String qrData =
"upi://pay?pa=$upiId&pn=$supplierName&am=$collectAmountInRupees&cu=INR&mam=1";
// --------------------------------------------
return
WillPopScope(
onWillPop: () async {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => AllOrders(navigationFrom:"")),
(route) => false,
);
return false;
},
child: Scaffold(
backgroundColor: Color(0XFFFFFFFF),
appBar: AppSettings.appBarWithNotificationIcon(
widget.details.building_name,
widget.details.type_of_water,
widget.details.capacity,
context,
),
body: SafeArea(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Padding(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 24),
child: Align(
alignment: const Alignment(0, -0.25),
child: Container(
width: double.infinity,
constraints: const BoxConstraints(maxWidth: 520),
padding: const EdgeInsets.fromLTRB(16, 18, 16, 16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFEDEDED)),
boxShadow: const [
BoxShadow(
color: Colors.black12,
blurRadius: 10,
offset: Offset(0, 4)),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 4),
Text(
'Collect money',
style: fontTextStyle(
16, const Color(0xFF7E7F80), FontWeight.w500),
),
const SizedBox(height: 6),
// AMOUNT
Text(
'$collectAmountInRupees',
style: fontTextStyle(
24, const Color(0xFF343637), FontWeight.w600),
),
const SizedBox(height: 16),
// --------------------------------------------
// 🔥 QR Code Box
// --------------------------------------------
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFFE0E0E0)),
),
padding: const EdgeInsets.all(12),
child: QrImageView(
data: qrData,
size: 200,
gapless: true,
backgroundColor: Colors.white,
),
),
const SizedBox(height: 18),
// CONTINUE BUTTON
SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton(
onPressed: () async {
AppSettings.preLoaderDialog(context);
bool isOnline =
await AppSettings.internetConnectivity();
if (!isOnline) {
Navigator.of(context, rootNavigator: true).pop();
AppSettings.longFailedToast(
"Please Check Internet");
return;
}
var payload = {
"amount_paid": widget.details.amount_paid,
"payment_mode": widget.details.payment_mode,
"orderStatus": "completed",
"deliveredDate": formatDeliveredDate(),
};
// 🔥 Call API
var response = await AppSettings.amountpaidByCustomer(
widget.details.bookingid, payload);
Navigator.of(context, rootNavigator: true)
.pop(); // CLOSE LOADER
if (response.isNotEmpty) {
// Decode JSON
var json = jsonDecode(response);
if (json["status_code"] == 200) {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => AllOrders(navigationFrom:"")),
(route) => false,
);
return;
} else {}
AppSettings.longFailedToast(
json["msg"] ?? "Invalid OTP");
return;
}
// TODO: Navigate home
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.black,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
),
child: Text(
'Continue to Home',
style: fontTextStyle(14, Colors.white, FontWeight.w600),
),
),
),
],
),
),
),
),
),
),
));
}
}

File diff suppressed because it is too large Load Diff

@ -27,6 +27,30 @@ class OrdersModel {
String water_source_location='';
String remarks='';
String unload_start_time = '';
String bookingid = '';
String tank_name = '';
String tankLocation = '';
List<String> extraStatus = [];
double? sourceLat;
double? sourceLng;
double? buildingLat;
double? buildingLng;
String delivery_agent_mobile='';
String amount_due = '';
String amount_paid = '';
String supplier_upi_id = '9000950877@ybl';
String supplier_name = '';
String payment_mode = '';
String tanker_status = '';
String tanker_availability = '';
String start_time = '';
String stop_time = '';
DateTime? orderDate;
OrdersModel();
factory OrdersModel.fromJson(Map<String, dynamic> json){
@ -49,6 +73,8 @@ class OrdersModel {
rtvm.tanker_name = json['tankerName'] ?? '';
rtvm.water_source_location = json['water_source_location'] ?? '';
rtvm.remarks = json['remarks'] ?? '';
rtvm.delivery_agent_mobile=json['delivery_agent_mobile']??'';
// Split and trim
List<String> parts = rtvm.address.split(',').map((e) => e.trim()).toList();
@ -72,6 +98,84 @@ class OrdersModel {
(rtvm.distanceInMeters / 1000).toStringAsFixed(2),
);
rtvm.tank_name = json['tankName'] ?? '';
rtvm.tankLocation = json['tankLocation'] ?? '';
rtvm.amount_due = json['amount_due'] ?? '';
rtvm.amount_paid = json['amount_paid'] ?? '';
rtvm.supplier_name = json['supplierName'] ?? '';
rtvm.payment_mode = json['payment_mode'] ?? '';
rtvm.start_time = json['start_time'] ?? '';
rtvm.stop_time = json['stop_time'] ?? '';
if (rtvm.date.isNotEmpty) {
final parts = rtvm.date.split('-');
if (parts.length == 3) {
final day = int.tryParse(parts[0]) ?? 1;
final year = int.tryParse(parts[2]) ?? 1970;
final monthStr = parts[1].toLowerCase();
const monthMap = {
'jan': 1,
'feb': 2,
'mar': 3,
'apr': 4,
'may': 5,
'jun': 6,
'jul': 7,
'aug': 8,
'sep': 9,
'oct': 10,
'nov': 11,
'dec': 12,
};
final month = monthMap[monthStr];
if (month != null) {
rtvm.orderDate = DateTime(year, month, day);
}
}
}
// ---------------- EXTRA DATA ----------------
if (json['extra'] != null && json['extra'] is List) {
for (var item in json['extra']) {
if (item['status'] != null && item['status'] is List) {
List<String> statusList =
List<String>.from(item['status'].map((e) => e.toString()));
rtvm.extraStatus = statusList;
if (statusList.length >= 1) {
rtvm.tanker_status = statusList[0].toLowerCase();
}
if (statusList.length >= 2) {
rtvm.tanker_availability = statusList[1].toLowerCase();
}
}
if (item['source_location'] != null) {
rtvm.sourceLat =
(item['source_location']['latitude'] ?? 0).toDouble();
rtvm.sourceLng =
(item['source_location']['longitude'] ?? 0).toDouble();
}
if (item['building_location'] != null) {
rtvm.buildingLat =
(item['building_location']['latitude'] ?? 0).toDouble();
rtvm.buildingLng =
(item['building_location']['longitude'] ?? 0).toDouble();
}
}
}
return rtvm;
}
Map<String, dynamic> toJson() => {

@ -0,0 +1,381 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:supplier_new/orders/unloading_inprogress.dart';
import '../common/settings.dart';
import 'all_orders.dart';
class UnloadArrivalScreen extends StatefulWidget {
var details;
UnloadArrivalScreen({
this.details
});
@override
State<UnloadArrivalScreen> createState() => _UnloadArrivalScreenState();
}
class _UnloadArrivalScreenState extends State<UnloadArrivalScreen> with TickerProviderStateMixin{
final _c1 = TextEditingController();
final _c2 = TextEditingController();
final _c3 = TextEditingController();
final _c4 = TextEditingController();
final _f1 = FocusNode();
final _f2 = FocusNode();
final _f3 = FocusNode();
final _f4 = FocusNode();
bool get _otpReady =>
_c1.text.isNotEmpty &&
_c2.text.isNotEmpty &&
_c3.text.isNotEmpty &&
_c4.text.isNotEmpty;
late AnimationController _shakeController;
late Animation<double> _offsetAnimation;
bool _isOtpComplete = false;
void _checkOtpFilled() {
setState(() {
_isOtpComplete =
_c1.text.isNotEmpty &&
_c2.text.isNotEmpty &&
_c3.text.isNotEmpty &&
_c4.text.isNotEmpty;
});
}
@override
void dispose() {
_c1.dispose();
_c2.dispose();
_c3.dispose();
_c4.dispose();
_f1.dispose();
_f2.dispose();
_f3.dispose();
_f4.dispose();
_shakeController.dispose();
super.dispose();
}
@override
void initState() {
super.initState();
_shakeController = AnimationController(
duration: const Duration(milliseconds: 450),
vsync: this,
);
_offsetAnimation = Tween(begin: 0.0, end: 15.0)
.chain(CurveTween(curve: Curves.elasticIn))
.animate(_shakeController);
}
void _onDigit({
required String value,
required FocusNode current,
FocusNode? next,
FocusNode? prev,
}) {
if (value.length == 1 && next != null) {
next.requestFocus();
} else if (value.isEmpty && prev != null) {
prev.requestFocus();
}
setState(() {});
}
InputDecoration _otpDecoration(bool focused) => InputDecoration(
counterText: '',
contentPadding: const EdgeInsets.symmetric(vertical: 14),
filled: true,
fillColor: Colors.white,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Color(0xFFE0E0E0)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Colors.black, width: 1.2),
),
);
@override
Widget build(BuildContext context) {
return
WillPopScope(
onWillPop: () async {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => AllOrders(navigationFrom:"")),
(route) => false,
);
return false;},
child:Scaffold(
backgroundColor: Colors.white,
appBar: AppSettings.appBarWithNotificationIcon(widget.details.building_name, widget.details.type_of_water, widget.details.capacity, context),
body: Stack(
children: [
// Map background
Positioned.fill(
child: Image.asset(
'images/google_maps.png', // make sure this exists & is declared in pubspec.yaml
fit: BoxFit.cover,
),
),
// Success text
Positioned(
left: 16,
right: 16,
top: 8,
child: Padding(
padding: const EdgeInsets.only(top: 24, left: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('You have reached!',
style: fontTextStyle(
20, const Color(0xFF101214), FontWeight.w600)),
const SizedBox(height: 4),
Text('Hurray! You are on-time.',
style: fontTextStyle(
16, const Color(0xFF0A9E04), FontWeight.w500)),
],
),
),
),
// Center card (nudged up)
Align(
alignment: const Alignment(0, -0.6),
child: Container(
width: MediaQuery.of(context).size.width * 0.86,
padding: const EdgeInsets.fromLTRB(16, 18, 16, 16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: const [
BoxShadow(
color: Colors.black12,
blurRadius: 10,
offset: Offset(0, 4))
],
),
child: widget.details.tank_name!=''?Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(widget.details.tank_name!=''?"Unload Water in":'The user hasnt selected a tank. Please ask the user to select one.',
textAlign: TextAlign.center,
style: fontTextStyle(
16, const Color(0xFF939495), FontWeight.w500)),
const SizedBox(height: 4),
Visibility(
visible: widget.details.tank_name!='',
child: Text(widget.details.tank_name,
style: fontTextStyle(
20, const Color(0xFF101214), FontWeight.w600)),),
const SizedBox(height: 14),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Enter OTP to Start Unloading',
style: fontTextStyle(14, const Color(0xFF646566),
FontWeight.w500)),
const SizedBox(width: 6),
Icon(Icons.help_outline,
size: 16, color: Colors.grey.shade600),
],
),
const SizedBox(height: 14),
// OTP boxes
AnimatedBuilder(
animation: _shakeController,
builder: (context, child) {
return Transform.translate(
offset: Offset(_offsetAnimation.value, 0),
child: child,
);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_OtpBox(
controller: _c1,
focusNode: _f1,
onChanged: (v){
_onDigit(value: v, current: _f1, next: _f2);
_checkOtpFilled();
},
//onChanged: (v) => _onDigit(value: v, current: _f1, next: _f2),
decoration: _otpDecoration(_f1.hasFocus),
),
_OtpBox(
controller: _c2,
focusNode: _f2,
onChanged: (v){
_onDigit(value: v, current: _f2, next: _f3, prev: _f1);
_checkOtpFilled();
},
decoration: _otpDecoration(_f2.hasFocus),
),
_OtpBox(
controller: _c3,
focusNode: _f3,
onChanged: (v){
_onDigit(value: v, current: _f3, next: _f4, prev: _f2);
_checkOtpFilled();
},
decoration: _otpDecoration(_f3.hasFocus),
),
_OtpBox(
controller: _c4,
focusNode: _f4,
onChanged: (v){
_onDigit(value: v, current: _f4, prev: _f3);
_checkOtpFilled();
},
decoration: _otpDecoration(_f4.hasFocus),
),
],
),
),
const SizedBox(height: 16),
// Continue button -> navigate to UnloadingInProgressScreen
SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton(
onPressed: _isOtpComplete ? () async {
if (!_otpReady) return;
AppSettings.preLoaderDialog(context);
bool isOnline = await AppSettings.internetConnectivity();
if (!isOnline) {
Navigator.of(context, rootNavigator: true).pop();
AppSettings.longFailedToast("Please Check Internet");
return;
}
// Combine OTP
final otp = "${_c1.text}${_c2.text}${_c3.text}${_c4.text}";
var payload = {
"action": "start",
"percentage": "100",
"otp": otp,
};
// 🔥 Call API
var response = await AppSettings.verifyUnloadStartOTP(
widget.details.bookingid,
payload
);
Navigator.of(context, rootNavigator: true).pop(); // CLOSE LOADER
if (response.isNotEmpty) {
// Decode JSON
var json = jsonDecode(response);
if (json["status_code"] == 200) {
// OTP VERIFIED NAVIGATE
FocusScope.of(context).unfocus();
await Future.delayed(const Duration(milliseconds: 100));
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => UnloadingInProgressScreen(details: widget.details)),
(route) => false,
);
} else {
_shakeController.forward(from: 0); // 🔥 SHAKES OTP BOXES
Future.delayed(const Duration(milliseconds: 300), () {
_c1.clear();
_c2.clear();
_c3.clear();
_c4.clear();
_f1.requestFocus(); // Move focus back to first box
_checkOtpFilled(); // Disable button again
});
AppSettings.longFailedToast(json["msg"] ?? "Invalid OTP");
return;
}
} else {
AppSettings.longFailedToast("Something went wrong");
}
} : null,
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.resolveWith((_) => Colors.black),
foregroundColor:
MaterialStateProperty.resolveWith((_) => Colors.white),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(28)),
),
),
child: Text('Continue',
style: fontTextStyle(
16, const Color(0xFFFFFFFF), FontWeight.w400)),
),
),
],
):
Text(widget.details.tank_name!=''?"Unload Water in":'The user hasnt selected a tank. Please ask the user to select one.',
textAlign: TextAlign.center,
style: fontTextStyle(
16, const Color(0xFF939495), FontWeight.w500)),
),
),
],
),
));
}
}
class _OtpBox extends StatelessWidget {
final TextEditingController controller;
final FocusNode focusNode;
final ValueChanged<String> onChanged;
final InputDecoration decoration;
const _OtpBox({
required this.controller,
required this.focusNode,
required this.onChanged,
required this.decoration,
});
@override
Widget build(BuildContext context) {
return SizedBox(
width: 54,
child: TextField(
controller: controller,
focusNode: focusNode,
textAlign: TextAlign.center,
keyboardType: TextInputType.number,
maxLength: 1,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
decoration: decoration,
onChanged: onChanged,
),
);
}
}

@ -0,0 +1,328 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
import '../common/settings.dart';
import 'collect_money.dart';
TextStyle fts(double s, Color c, FontWeight w) =>
GoogleFonts.inter(fontSize: s, color: c, fontWeight: w);
class UnloadingCompleteScreen extends StatefulWidget {
var details;
UnloadingCompleteScreen({
this.details,
});
@override
State<UnloadingCompleteScreen> createState() => _UnloadingCompleteScreenState();
}
class _UnloadingCompleteScreenState extends State<UnloadingCompleteScreen> with TickerProviderStateMixin{
final _c1 = TextEditingController();
final _c2 = TextEditingController();
final _c3 = TextEditingController();
final _c4 = TextEditingController();
final _f1 = FocusNode();
final _f2 = FocusNode();
final _f3 = FocusNode();
final _f4 = FocusNode();
bool get _otpReady =>
_c1.text.isNotEmpty && _c2.text.isNotEmpty && _c3.text.isNotEmpty && _c4.text.isNotEmpty;
late AnimationController _shakeController;
late Animation<double> _offsetAnimation;
bool _isOtpComplete = false;
void _checkOtpFilled() {
setState(() {
_isOtpComplete =
_c1.text.isNotEmpty &&
_c2.text.isNotEmpty &&
_c3.text.isNotEmpty &&
_c4.text.isNotEmpty;
});
}
@override
void initState() {
super.initState();
_shakeController = AnimationController(
duration: const Duration(milliseconds: 450),
vsync: this,
);
_offsetAnimation = Tween(begin: 0.0, end: 15.0)
.chain(CurveTween(curve: Curves.elasticIn))
.animate(_shakeController);
}
@override
void dispose() {
_c1.dispose();
_c2.dispose();
_c3.dispose();
_c4.dispose();
_f1.dispose();
_f2.dispose();
_f3.dispose();
_f4.dispose();
_shakeController.dispose();
super.dispose();
}
void _onDigit({
required String value,
required FocusNode current,
FocusNode? next,
FocusNode? prev,
}) {
if (value.length == 1 && next != null) {
next.requestFocus();
} else if (value.isEmpty && prev != null) {
prev.requestFocus();
}
setState(() {});
}
InputDecoration _otpDecoration(bool focused) => InputDecoration(
counterText: '',
contentPadding: const EdgeInsets.symmetric(vertical: 14),
filled: true,
fillColor: Colors.white,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Color(0xFFE0E0E0)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Colors.black, width: 1.2),
),
);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppSettings.appBarWithNotificationIcon(widget.details.building_name, widget.details.type_of_water, widget.details.capacity, context),
body: SafeArea(
child: Align(
alignment: const Alignment(0, -0.25),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container(
width: double.infinity,
constraints: const BoxConstraints(maxWidth: 520),
padding: const EdgeInsets.fromLTRB(16, 18, 16, 16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 10, offset: Offset(0, 4))],
border: Border.all(color: const Color(0xFFEDEDED)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Green check
Container(
width: 28,
height: 28,
decoration: const BoxDecoration(color: Color(0xFF2FAE22), shape: BoxShape.circle),
child: const Icon(Icons.check, color: Colors.white, size: 18),
),
const SizedBox(height: 12),
Text('Unloading Complete', style: fts(20, const Color(0xFF101214), FontWeight.w700)),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Enter OTP to finish delivery', style: fts(12, const Color(0xFF7E7F80), FontWeight.w500)),
const SizedBox(width: 6),
Icon(Icons.help_outline, size: 16, color: Colors.grey.shade600),
],
),
const SizedBox(height: 14),
// OTP row
AnimatedBuilder(
animation: _shakeController,
builder: (context, child) {
return Transform.translate(
offset: Offset(_offsetAnimation.value, 0),
child: child,
);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_OtpBox(
controller: _c1,
focusNode: _f1,
onChanged: (v){
_onDigit(value: v, current: _f1, next: _f2);
_checkOtpFilled();
},
//onChanged: (v) => _onDigit(value: v, current: _f1, next: _f2),
decoration: _otpDecoration(_f1.hasFocus),
),
_OtpBox(
controller: _c2,
focusNode: _f2,
onChanged: (v){
_onDigit(value: v, current: _f2, next: _f3, prev: _f1);
_checkOtpFilled();
},
decoration: _otpDecoration(_f2.hasFocus),
),
_OtpBox(
controller: _c3,
focusNode: _f3,
onChanged: (v){
_onDigit(value: v, current: _f3, next: _f4, prev: _f2);
_checkOtpFilled();
},
decoration: _otpDecoration(_f3.hasFocus),
),
_OtpBox(
controller: _c4,
focusNode: _f4,
onChanged: (v){
_onDigit(value: v, current: _f4, prev: _f3);
_checkOtpFilled();
},
decoration: _otpDecoration(_f4.hasFocus),
),
],
),
),
const SizedBox(height: 16),
// Continue button -> go to CollectMoneyScreen
SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton(
onPressed: _isOtpComplete ? () async {
if (!_otpReady) return;
AppSettings.preLoaderDialog(context);
bool isOnline = await AppSettings.internetConnectivity();
if (!isOnline) {
Navigator.of(context, rootNavigator: true).pop();
AppSettings.longFailedToast("Please Check Internet");
return;
}
// Combine OTP
final otp = "${_c1.text}${_c2.text}${_c3.text}${_c4.text}";
var payload = {
"action": "stop",
"percentage": "100",
"otp": otp,
};
// 🔥 Call API
var response = await AppSettings.verifyUnloadStartOTP(
widget.details.bookingid,
payload
);
Navigator.of(context, rootNavigator: true).pop(); // CLOSE LOADER
if (response.isNotEmpty) {
// Decode JSON
var json = jsonDecode(response);
if (json["status_code"] == 200) {
// OTP VERIFIED NAVIGATE
FocusScope.of(context).unfocus();
await Future.delayed(const Duration(milliseconds: 100));
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => CollectMoney(details:widget.details)),
);
} else {
// OTP WRONG
/*FocusScope.of(context).unfocus();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UnloadingInProgressScreen(details: widget.details),
),
);*/
_shakeController.forward(from: 0); // 🔥 SHAKES OTP BOXES
Future.delayed(const Duration(milliseconds: 300), () {
_c1.clear();
_c2.clear();
_c3.clear();
_c4.clear();
_f1.requestFocus(); // Move focus back to first box
_checkOtpFilled(); // Disable button again
});
AppSettings.longFailedToast(json["msg"] ?? "Invalid OTP");
return;
}
} else {
AppSettings.longFailedToast("Something went wrong");
}
} : null,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith((_) => Colors.black), // always black
foregroundColor: MaterialStateProperty.resolveWith((_) => Colors.white),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
),
),
child: Text('Continue', style: fts(14, Colors.white, FontWeight.w600)),
),
),
],
),
),
),
),
),
);
}
}
class _OtpBox extends StatelessWidget {
final TextEditingController controller;
final FocusNode focusNode;
final ValueChanged<String> onChanged;
final InputDecoration decoration;
const _OtpBox({
required this.controller,
required this.focusNode,
required this.onChanged,
required this.decoration,
});
@override
Widget build(BuildContext context) {
return SizedBox(
width: 54,
child: TextField(
controller: controller,
focusNode: focusNode,
textAlign: TextAlign.center,
keyboardType: TextInputType.number,
maxLength: 1,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
decoration: decoration,
onChanged: onChanged,
),
);
}
}

@ -0,0 +1,120 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:supplier_new/orders/unloading_completed_otp.dart';
import '../common/settings.dart';
import 'all_orders.dart';
TextStyle fts(double s, Color c, FontWeight w) =>
GoogleFonts.inter(fontSize: s, color: c, fontWeight: w);
class UnloadingInProgressScreen extends StatefulWidget {
final Duration? startFrom;
var details;
UnloadingInProgressScreen({
this.details,
this.startFrom
});
/// If you want to start from a preset time (e.g., 12 minutes), pass Duration(minutes: 12)
@override
State<UnloadingInProgressScreen> createState() => _UnloadingInProgressScreenState();
}
class _UnloadingInProgressScreenState extends State<UnloadingInProgressScreen> {
late Duration _elapsed;
Timer? _timer;
@override
void initState() {
super.initState();
_elapsed = widget.startFrom ?? Duration.zero;
_timer = Timer.periodic(const Duration(seconds: 1), (_) {
setState(() => _elapsed += const Duration(seconds: 1));
});
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
String _fmt(Duration d) {
final h = d.inHours.toString().padLeft(2, '0');
final m = (d.inMinutes % 60).toString().padLeft(2, '0');
final s = (d.inSeconds % 60).toString().padLeft(2, '0');
return '$h:$m:$s';
}
void _onComplete() {
_timer?.cancel();
// Optionally show a toast/snackbar, then navigate
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(content: Text('Unloading marked complete at ${_fmt(_elapsed)}')),
// );
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => UnloadingCompleteScreen(details: widget.details,)),
);
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => AllOrders(navigationFrom:"")),
(route) => false,
);
return false;
},
child:Scaffold(
backgroundColor: Colors.white,
appBar: AppSettings.appBarWithNotificationIcon(widget.details.building_name, widget.details.type_of_water, widget.details.capacity, context),
body: SafeArea(
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Unloading in-progress',
style: fontTextStyle(24, const Color(0xFF000000), FontWeight.w500)),
const SizedBox(height: 12),
Text(
_fmt(_elapsed),
style: fontTextStyle(40, const Color(0xFF000000), FontWeight.w700),
),
const SizedBox(height: 24),
SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton(
onPressed: _onComplete,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.black,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
),
child: Text('Unloading Complete',
style: fontTextStyle(14, const Color(0xFFFFFFFF), FontWeight.w400)),
),
),
],
),
),
),
),
));
}
}

@ -896,6 +896,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.1.5+1"
qr:
dependency: transitive
description:
name: qr
sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
qr_flutter:
dependency: "direct main"
description:
name: qr_flutter
sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097"
url: "https://pub.dev"
source: hosted
version: "4.1.0"
sanitize_html:
dependency: transitive
description:

@ -37,6 +37,7 @@ dependencies:
photo_view: ^0.15.0
url_launcher: ^6.1.9
sms_autofill: ^2.4.0
qr_flutter: ^4.1.0
dev_dependencies:
flutter_test:

Loading…
Cancel
Save