import ' dart:async ' ;
import ' dart:convert ' ;
import ' package:flutter/material.dart ' ;
import ' package:intl/intl.dart ' ;
import ' package:shared_preferences/shared_preferences.dart ' ;
import ' package:bookatanker/common/settings.dart ' ;
import ' package:dropdown_button2/dropdown_button2.dart ' ;
import ' package:bookatanker/supplier/filter_screen.dart ' ;
import ' package:bookatanker/supplier/supplier_details.dart ' ;
import ' package:bookatanker/supplier/supplier_details_from_search.dart ' ;
import ' package:bookatanker/supplier/suppliers_model.dart ' ;
class BookATanker extends StatefulWidget {
const BookATanker ( { super . key } ) ;
@ override
State < BookATanker > createState ( ) = > _BookATankerState ( ) ;
}
class _BookATankerState extends State < BookATanker > {
String ? selectedWaterType ;
String ? selectedCapacity ;
String ? selectedQuantity ;
String ? selectedTime ;
DateTime ? selectedDate ;
List < SuppliersModel > SuppliersList = [ ] ;
bool isDataLoading = false ;
bool isSearchEnabled = false ;
int sentRequests = 0 ;
int maxRequests = 5 ;
DateTime ? firstRequestTime ;
Timer ? _resetTimer ;
Timer ? _countdownTimer ;
String remainingTime = ' ' ;
double progress = 1.0 ;
int totalCooldown = 900 ; // 15 minutes in seconds
RangeValues ? selectedRadius ;
RangeValues ? selectedRating ;
RangeValues ? selectedPrice ;
String ? selectedPump ;
bool filterAll = true , filterConnected = false , filterNotConnected = false ;
final TextEditingController _quotedAmountController = TextEditingController ( ) ;
// TimeOfDay _selectedTime = (TimeOfDay.now());
final List < String > waterTypes = [ ' Drinking Water ' , ' Bore water ' ] ;
final List < String > capacities = [
' 1,000 L ' ,
' 2,000 L ' ,
' 3,000 L ' ,
' 5,000 L ' ,
' 10,000 L ' ,
' 20,000 L '
] ;
final List < String > quantities = [ ' 1 ' , ' 2 ' , ' 3 ' ] ;
final List < String > timeSlots = [ ' 8 AM ' , ' 12 PM ' , ' 4 PM ' , ' 8 PM ' ] ;
@ override
void initState ( ) {
// TODO: implement initState
super . initState ( ) ;
_selectedTime = _roundToNextHour ( TimeOfDay . now ( ) ) ;
_loadRequestState ( ) ;
// Defer the bottom sheet call until after build
WidgetsBinding . instance . addPostFrameCallback ( ( _ ) {
_showLocationBottomSheet ( ) ;
} ) ;
}
@ override
void dispose ( ) {
_countdownTimer ? . cancel ( ) ;
_quotedAmountController . dispose ( ) ;
super . dispose ( ) ;
}
Future < void > _loadRequestState ( ) async {
List < DateTime > validTimes = await _getValidRequestTimes ( ) ;
validTimes . sort ( ) ; // Ensure chronological order
setState ( ( ) {
sentRequests = validTimes . length ;
} ) ;
if ( validTimes . isNotEmpty ) {
final firstExpiry = validTimes . first . add ( Duration ( minutes: 15 ) ) ;
final remaining = firstExpiry . difference ( DateTime . now ( ) ) . inSeconds ;
if ( remaining > 0 ) {
_startCountdownTimer ( remaining ) ;
}
}
}
Future < List < DateTime > > _getValidRequestTimes ( ) async {
SharedPreferences prefs = await SharedPreferences . getInstance ( ) ;
final rawList = prefs . getStringList ( ' requestTimes ' ) ? ? [ ] ;
final now = DateTime . now ( ) ;
final validTimes = rawList
. map ( ( s ) = > DateTime . tryParse ( s ) )
. whereType < DateTime > ( )
. where ( ( t ) = > now . difference ( t ) . inMinutes < 15 )
. toList ( ) ;
return validTimes ;
}
Future < void > _saveRequestTimes ( List < DateTime > times ) async {
SharedPreferences prefs = await SharedPreferences . getInstance ( ) ;
prefs . setStringList ( ' requestTimes ' , times . map ( ( e ) = > e . toIso8601String ( ) ) . toList ( ) ) ;
}
void _startCountdownTimer ( int totalSeconds ) {
_countdownTimer ? . cancel ( ) ;
totalCooldown = totalSeconds ;
progress = 1.0 ;
_countdownTimer = Timer . periodic ( Duration ( seconds: 1 ) , ( timer ) async {
final elapsed = timer . tick ;
final remaining = totalSeconds - elapsed ;
if ( remaining < = 0 ) {
timer . cancel ( ) ;
// 🧹 Clean expired request times
List < DateTime > updatedTimes = await _getValidRequestTimes ( ) ;
updatedTimes . sort ( ) ; // Ensure order
await _saveRequestTimes ( updatedTimes ) ;
setState ( ( ) {
sentRequests = updatedTimes . length ;
remainingTime = ' ' ;
progress = 0.0 ;
} ) ;
// 🕐 If more valid requests remain, restart countdown with new first one
if ( updatedTimes . isNotEmpty ) {
final nextExpiry = updatedTimes . first . add ( Duration ( minutes: 15 ) ) ;
final newRemaining = nextExpiry . difference ( DateTime . now ( ) ) . inSeconds ;
if ( newRemaining > 0 ) {
_startCountdownTimer ( newRemaining ) ;
}
}
} else {
final minutes = ( remaining ~ / 60 ) . toString ( ) . padLeft ( 2 , ' 0 ' ) ;
final seconds = ( remaining % 60 ) . toString ( ) . padLeft ( 2 , ' 0 ' ) ;
setState ( ( ) {
remainingTime = " $ minutes : $ seconds " ;
progress = remaining / totalCooldown ;
} ) ;
}
} ) ;
}
void _showLocationBottomSheet ( ) {
showModalBottomSheet (
context: context ,
isScrollControlled: true ,
isDismissible: false ,
enableDrag: false , // 🔒 Prevents dragging
shape: RoundedRectangleBorder (
borderRadius: BorderRadius . vertical ( top: Radius . circular ( 16 ) ) ,
) ,
builder: ( context ) {
return Padding (
padding: EdgeInsets . only (
bottom: MediaQuery . of ( context ) . viewInsets . bottom ,
) ,
child: Container (
padding: EdgeInsets . all ( 16 ) ,
constraints: BoxConstraints (
maxHeight: MediaQuery . of ( context ) . size . height * 0.45 ,
) ,
decoration: BoxDecoration (
color: Colors . white ,
borderRadius: BorderRadius . vertical ( top: Radius . circular ( 16 ) ) ,
) ,
child: SingleChildScrollView (
child: Column (
mainAxisSize: MainAxisSize . min ,
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
Text (
" Confirm Address? " ,
style: fontTextStyle ( 16 , Color ( 0XFF343637 ) , FontWeight . w600 ) ,
) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 024 ) ,
Row (
children: [
Text (
" Your current selected address is " ,
style: fontTextStyle ( 12 , Color ( 0XFF515253 ) , FontWeight . w600 ) ,
) ,
SizedBox ( width: MediaQuery . of ( context ) . size . width * . 004 ) ,
Text (
" HOME " ,
style: fontTextStyle ( 12 , Color ( 0XFF2D2E30 ) , FontWeight . w600 ) ,
) ,
] ,
) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 012 ) ,
Row (
children: [
Image . asset (
' images/marker-pin.png ' ,
width: 24 ,
height: 24 ,
) ,
SizedBox ( width: MediaQuery . of ( context ) . size . width * . 024 ) ,
Expanded (
child: Text (
AppSettings . userAddress ,
style: fontTextStyle ( 12 , Color ( 0XFF515253 ) , FontWeight . w400 ) ,
) ,
)
] ,
) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 012 ) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
Expanded (
child: GestureDetector (
onTap: ( ) {
// Optional: handle "Change"
} ,
child: Container (
decoration: BoxDecoration (
color: Colors . white ,
border: Border . all ( width: 1 , color: Color ( 0XFF939495 ) ) ,
borderRadius: BorderRadius . circular ( 24 ) ,
) ,
alignment: Alignment . center ,
padding: EdgeInsets . symmetric ( vertical: 12 ) ,
child: Text (
' Change ' ,
style: fontTextStyle ( 14 , Color ( 0XFF939495 ) , FontWeight . w600 ) ,
) ,
) ,
) ,
) ,
SizedBox ( width: MediaQuery . of ( context ) . size . width * . 010 ) ,
Expanded (
child: GestureDetector (
onTap: ( ) {
Navigator . pop ( context ) ;
/ * setState ( ( ) {
_isConfirmed = true ;
} ) ; * /
} ,
child: Container (
decoration: BoxDecoration (
color: Color ( 0XFF1D7AFC ) ,
border: Border . all ( width: 1 , color: Colors . white ) ,
borderRadius: BorderRadius . circular ( 24 ) ,
) ,
alignment: Alignment . center ,
padding: EdgeInsets . symmetric ( vertical: 12 ) ,
child: Text (
' Confirm ' ,
style: fontTextStyle ( 14 , Colors . white , FontWeight . w600 ) ,
) ,
) ,
) ,
) ,
] ,
) ,
] ,
) ,
) ,
) ,
) ;
} ,
) ;
}
TimeOfDay _roundToNextHour ( TimeOfDay time ) {
int nextHour = ( time . minute > 0 ) ? ( time . hour + 1 ) % 24 : time . hour ;
return TimeOfDay ( hour: nextHour , minute: 0 ) ;
}
void sendRequest ( ) {
if ( sentRequests < maxRequests ) {
setState ( ( ) {
sentRequests + + ;
} ) ;
}
}
Future < void > _selectFromDate ( BuildContext context ) async {
final DateTime ? picked = await showDatePicker (
context: context ,
initialDate: selectedDate ? ? DateTime . now ( ) ,
firstDate: DateTime . now ( ) ,
lastDate: DateTime . now ( ) . add ( Duration ( days: 3 ) ) ,
helpText: ' Select Date ' ,
initialEntryMode: DatePickerEntryMode . calendarOnly ,
builder: ( BuildContext context , Widget ? child ) {
return Theme (
data: ThemeData . light ( ) . copyWith (
inputDecorationTheme: InputDecorationTheme (
border: InputBorder . none , // Removes the text field border
) ,
colorScheme: ColorScheme . light (
primary: Color ( 0XFF1D7AFC ) ,
onPrimary: Color ( 0XFFFFFFFF ) , // Header text color
surface: Color ( 0XFFFFFFFF ) ,
onSurface: Colors . black ,
secondary: Colors . pink ,
) ,
dividerColor: Color ( 0XFFF5F6F6 ) ,
textButtonTheme: TextButtonThemeData (
style: ButtonStyle (
foregroundColor: MaterialStateProperty . all (
Color ( 0XFF1D7AFC ) ,
) , // Text color
//backgroundColor: MaterialStateProperty.all(Colors.grey[200]), // Background
textStyle: MaterialStateProperty . all (
fontTextStyle ( 14 , Color ( 0XFF1D7AFC ) , FontWeight . w600 ) ,
) , // Font style
shape: MaterialStateProperty . all ( RoundedRectangleBorder (
borderRadius: BorderRadius . circular ( 8 ) ) ) , // Rounded corners
) ,
// Background of the dialog box
) ,
textTheme: TextTheme (
bodyLarge: fontTextStyle ( 14 , Color ( 0XFF1D7AFC ) , FontWeight . w600 ) ,
labelLarge: fontTextStyle ( 14 , Color ( 0XFF1D7AFC ) , FontWeight . w600 ) ,
titleLarge: fontTextStyle ( 14 , Color ( 0XFF1D7AFC ) , FontWeight . w600 ) ,
headlineLarge:
fontTextStyle ( 20 , Color ( 0XFF1D7AFC ) , FontWeight . w600 ) ,
) ,
) ,
child: child ! ,
) ;
} ,
) ;
if ( picked ! = null ) {
setState ( ( ) {
selectedDate = picked ;
//toDate1 = null; // Reset To Date
//toDateController1.clear();
} ) ;
}
}
TimeOfDay _selectedTime = TimeOfDay . now ( ) ;
String _timeRangeText = ' ' ;
Future < void > _selectTime ( BuildContext context ) async {
final TimeOfDay ? picked = await showTimePicker (
context: context ,
initialTime: _selectedTime ,
cancelText: ' Cancel ' ,
confirmText: ' Confirm ' ,
builder: ( BuildContext context , Widget ? child ) {
return Theme (
data: ThemeData . light ( ) . copyWith (
primaryColor: Color ( 0XFF1D7AFC ) ,
timePickerTheme: TimePickerThemeData (
backgroundColor: Colors . white ,
dialBackgroundColor: Colors . white ,
hourMinuteTextColor: Color ( 0XFF1D7AFC ) ,
dayPeriodTextColor: Color ( 0XFF1D7AFC ) ,
dialTextColor: Color ( 0XFF1D7AFC ) ,
dayPeriodColor: Color ( 0XFFC3C4C4 ) ,
hourMinuteShape: RoundedRectangleBorder (
borderRadius: BorderRadius . circular ( 20 ) ,
side: BorderSide ( color: Color ( 0XFFFFFFFF ) , width: 2 ) ,
) ,
dayPeriodShape: RoundedRectangleBorder (
borderRadius: BorderRadius . circular ( 12 ) ) ,
dialHandColor: Color ( 0XFFC3C4C4 ) ,
hourMinuteColor: Color ( 0XFFFFFFFF ) ,
dialTextStyle:
TextStyle ( fontSize: 14 , fontWeight: FontWeight . w600 ) ,
dayPeriodTextStyle:
TextStyle ( fontSize: 14 , fontWeight: FontWeight . w600 ) ,
hourMinuteTextStyle:
TextStyle ( fontSize: 50 , fontWeight: FontWeight . w600 ) ,
helpTextStyle:
TextStyle ( fontSize: 14 , fontWeight: FontWeight . w600 ) ,
) ,
) ,
child: child ! ,
) ;
} ,
) ;
if ( picked ! = null ) {
final now = TimeOfDay . now ( ) ;
final today = DateTime . now ( ) ;
final isToday = selectedDate ? . day = = today . day & &
selectedDate ? . month = = today . month & &
selectedDate ? . year = = today . year ;
bool isValid = true ;
if ( isToday ) {
final nowMinutes = now . hour * 60 + now . minute ;
final pickedMinutes = picked . hour * 60 + picked . minute ;
isValid = pickedMinutes > = nowMinutes ;
}
if ( isValid ) {
setState ( ( ) {
_selectedTime = picked ;
_timeRangeText = _formatTimeRange ( _selectedTime ) ;
} ) ;
} else {
ScaffoldMessenger . of ( context ) . showSnackBar (
SnackBar (
content: Text ( " Please select a time after the current time. " ) ) ,
) ;
}
}
}
String _formatTimeRange ( TimeOfDay start ) {
final endHour = ( start . hour + 2 ) % 24 ;
final end = TimeOfDay ( hour: endHour , minute: start . minute ) ;
return ' ${ _formatTime ( start ) } to ${ _formatTime ( end ) } ' ;
}
String _formatTime ( TimeOfDay time ) {
final now = DateTime . now ( ) ;
final dt = DateTime ( now . year , now . month , now . day , time . hour , time . minute ) ;
return TimeOfDay . fromDateTime ( dt ) . format ( context ) ;
}
Widget _beforeDataScreen ( ) {
return Padding ( padding: EdgeInsets . fromLTRB ( 16 , 0 , 16 , 0 ) ,
child: Column (
children: [
Align (
alignment: Alignment . centerLeft ,
child: Text (
" What’ s new? " ,
style: fontTextStyle ( 12 , Color ( 0XFF515253 ) , FontWeight . w400 ) ,
) ,
) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 012 ) ,
RichText (
text: TextSpan (
text: " Find " ,
style: fontTextStyle ( 20 , Color ( 0XFF343637 ) , FontWeight . w600 ) ,
children: [
TextSpan (
text: " exclusive offers " ,
style: fontTextStyle ( 20 , Color ( 0XFF8270DB ) , FontWeight . w600 ) ,
) ,
TextSpan (
text: " & best deals available for you. " ,
style: fontTextStyle ( 20 , Color ( 0XFF343637 ) , FontWeight . w600 ) ,
) ,
] ,
) ,
) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 024 ) ,
Container (
height: 80 ,
width: double . infinity ,
padding: EdgeInsets . symmetric ( horizontal: 12 ) ,
decoration: BoxDecoration (
borderRadius: BorderRadius . circular ( 24 ) ,
gradient: LinearGradient (
colors: [
Color ( 0xFFFFFFFF ) ,
Color ( 0xFFE2DCFF )
] , // Light gradient background
begin: Alignment . centerLeft ,
end: Alignment . centerRight ,
) ,
border: Border . all ( color: Color ( 0xFF8270DB ) ) ,
) ,
child: Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
Column (
mainAxisAlignment: MainAxisAlignment . center ,
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
Text (
' AQUINTO ' ,
style:
fontTextStyle ( 20 , Color ( 0XFF8270DB ) , FontWeight . w800 ) ,
) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 004 ) ,
Text (
' Your Water, Your Data, Your Control. ' ,
style:
fontTextStyle ( 12 , Color ( 0XFF8270DB ) , FontWeight . w500 ) ,
) ,
] ,
) ,
/ * Image . asset (
' images/WaterTankImage.png ' , // Replace with your actual image asset path
height: 40 ,
width: 40 ,
fit: BoxFit . contain ,
) , * /
] ,
) ,
)
] ,
) , ) ;
}
Widget _beforeDataScreenAbove ( ) {
return Column (
children: [
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 024 ) ,
CircleAvatar ( radius: 80 , backgroundColor: Colors . grey . shade300 ) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 016 ) ,
Center (
child: Text ( " Book Tanker " ,
style: fontTextStyle ( 20 , Color ( 0XFF000000 ) , FontWeight . w600 ) ) ,
) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 008 ) ,
Text (
" Book a tanker instantly or schedule it \n anytime in the next 3 days " ,
textAlign: TextAlign . center ,
style: fontTextStyle ( 12 , Color ( 0XFF515253 ) , FontWeight . w400 ) ,
) ,
] ,
) ;
}
Widget _suppliersDataScreen ( ) {
if ( isDataLoading ) {
return Center (
child: CircularProgressIndicator (
color: primaryColor ,
strokeWidth: 5.0 ,
) ,
) ;
}
if ( SuppliersList . isEmpty ) {
return Center (
child: Text (
' No Data Available ' ,
style: fontTextStyle ( 12 , Color ( 0XFF000000 ) , FontWeight . w500 ) ,
) ,
) ;
}
return Container (
color: Color ( 0xFFF1F1F1 ) , // Set your desired background color
padding: EdgeInsets . fromLTRB ( 16 , 0 , 16 , 0 ) ,
child: Column (
children: [
Column (
children: [
Container (
padding: EdgeInsets . symmetric ( horizontal: 0 , vertical: 12 ) ,
decoration: BoxDecoration (
color: Color ( 0xFFF1F1F1 ) ,
borderRadius: BorderRadius . circular ( 8 ) ,
) ,
child: Column (
children: [
Row (
children: [
Image . asset (
' images/info.png ' ,
fit: BoxFit . cover ,
width:
16 , // Match the diameter of the CircleAvatar
height: 16 ,
) ,
SizedBox (
width: MediaQuery . of ( context ) . size . width * . 008 ,
) ,
Text (
" Order requests sent " ,
style: fontTextStyle ( 14 , Color ( 0XFF2D2E30 ) , FontWeight . w600 ) ,
) ,
SizedBox (
width: MediaQuery . of ( context ) . size . width * . 012 ,
) ,
Text ( " $ sentRequests / $ maxRequests " , style: fontTextStyle ( 14 , Color ( 0XFF515253 ) , FontWeight . w600 ) , ) ,
Spacer ( ) ,
Text ( " $ remainingTime " , style: fontTextStyle ( 12 , Color ( 0XFF515253 ) , FontWeight . w400 ) , ) ,
] ,
) ,
SizedBox (
height: MediaQuery . of ( context ) . size . height * . 016 ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceEvenly ,
children: List . generate ( maxRequests , ( index ) {
return Expanded (
child: Container (
margin: EdgeInsets . symmetric ( horizontal: 4 ) ,
height: 3 ,
decoration: BoxDecoration (
color: index < sentRequests
? Color ( 0XFF114690 )
: Color ( 0XFF939495 ) ,
borderRadius: BorderRadius . circular ( 2 ) ,
) ,
) ,
) ;
} ) ,
) ,
Padding (
padding: EdgeInsets . only ( top: 12 , right: 0 ) ,
child: Row (
mainAxisAlignment: MainAxisAlignment . end ,
children: [
GestureDetector (
onTap: ( ) { } ,
child: Container (
padding: EdgeInsets . symmetric ( horizontal: 16 , vertical: 12 ) ,
decoration: BoxDecoration (
border: Border . all ( color: Color ( 0XFF515253 ) ) ,
borderRadius: BorderRadius . circular ( 45 ) ,
) ,
child: Row (
mainAxisSize: MainAxisSize . min ,
children: [
Image . asset (
' images/sort.png ' ,
width: 12 ,
height: 12 ,
color: Color ( 0XFF515253 ) ,
fit: BoxFit . contain ,
) ,
SizedBox ( width: MediaQuery . of ( context ) . size . width * . 008 ) ,
Text (
" Sort " ,
style: fontTextStyle ( 12 , Color ( 0XFF515253 ) , FontWeight . w400 ) ,
) ,
SizedBox ( width: MediaQuery . of ( context ) . size . width * . 008 ) ,
Image . asset (
' images/arrow_down.png ' ,
width: 16 ,
height: 16 ,
color: Color ( 0XFF515253 ) ,
fit: BoxFit . contain ,
) ,
] ,
) ,
) ,
) ,
SizedBox (
width: MediaQuery . of ( context ) . size . width * . 016 ,
) ,
GestureDetector (
onTap: ( ) async {
final result = await Navigator . push (
context ,
MaterialPageRoute (
builder: ( _ ) = > FilterScreen (
initialFilters: {
' all ' : filterAll ,
' connected ' : filterConnected ,
' notConnected ' : filterNotConnected ,
' radiusRange ' : selectedRadius ,
' ratingRange ' : selectedRating ,
' priceRange ' : selectedPrice ,
' pumpChoice ' : selectedPump ,
} ,
) ,
) ,
) ;
if ( result ! = null & & result is Map ) {
setState ( ( ) {
filterAll = result [ ' all ' ] ;
filterConnected = result [ ' connected ' ] ;
filterNotConnected = result [ ' notConnected ' ] ;
selectedRadius = result [ ' radiusRange ' ] ;
selectedRating = result [ ' ratingRange ' ] ;
selectedPrice = result [ ' priceRange ' ] ;
selectedPump = result [ ' pumpChoice ' ] ;
} ) ;
await getAllSuppliers ( ) ;
}
} ,
child: Container (
padding: EdgeInsets . symmetric ( horizontal: 16 , vertical: 12 ) ,
decoration: BoxDecoration (
border: Border . all ( color: Color ( 0XFF515253 ) ) ,
borderRadius: BorderRadius . circular ( 45 ) ,
) ,
child: Row (
mainAxisSize: MainAxisSize . min ,
children: [
Image . asset (
' images/filter.png ' ,
width: 12 ,
height: 12 ,
color: Color ( 0XFF515253 ) ,
fit: BoxFit . contain ,
) ,
SizedBox ( width: MediaQuery . of ( context ) . size . width * . 008 ) ,
Text (
" Filter " ,
style: fontTextStyle ( 12 , Color ( 0XFF515253 ) , FontWeight . w400 ) ,
) ,
] ,
) ,
) ,
)
] ,
) ,
)
] ,
) ) ,
] ,
) ,
ListView . separated (
shrinkWrap: true ,
physics: NeverScrollableScrollPhysics ( ) ,
padding: EdgeInsets . zero ,
itemCount: SuppliersList . length ,
separatorBuilder: ( context , index ) = > SizedBox (
height: MediaQuery . of ( context ) . size . height * . 008 ,
) ,
itemBuilder: ( BuildContext context , int index ) {
//final supplier = SuppliersList[index];
return GestureDetector (
onTap: ( ) {
Navigator . push (
context ,
MaterialPageRoute (
builder: ( context ) = > SupplierScreenFromSearch ( details: SuppliersList [ index ] , ) ) ,
) ;
} ,
child: Card (
color: Colors . white ,
elevation: 2 ,
shape: RoundedRectangleBorder (
borderRadius: BorderRadius . circular ( 12 ) ,
) ,
child: Padding (
padding: EdgeInsets . all ( 12 ) ,
child: Column (
children: [
Row (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
CircleAvatar (
radius: 25 ,
backgroundColor: Color ( 0XFFE8F2FF ) ,
child: Image . asset (
' images/profile_user.png ' ,
fit: BoxFit . cover ,
width: 50 ,
height: 50 ,
) ,
) ,
SizedBox (
width: MediaQuery . of ( context ) . size . width * . 024 ,
) ,
Expanded (
child: Column (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
/// Name and Rating Row
Row (
mainAxisAlignment:
MainAxisAlignment . spaceBetween ,
children: [
Expanded (
child: Text (
SuppliersList [ index ] . supplier_name ,
style: fontTextStyle ( 16 ,
Color ( 0XFF2D2E30 ) , FontWeight . w600 ) ,
overflow: TextOverflow . ellipsis ,
) ,
) ,
Row (
children: [
Image . asset (
' images/star.png ' ,
width: 16 ,
height: 16 ,
) ,
SizedBox (
width:
MediaQuery . of ( context ) . size . width *
. 008 ,
) ,
Text (
' 4.2 ' ,
style: fontTextStyle ( 10 ,
Color ( 0XFF515253 ) , FontWeight . w400 ) ,
) ,
] ,
) ,
] ,
) ,
SizedBox (
height:
MediaQuery . of ( context ) . size . height * . 004 ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
Text (
' Drinking | Bore Water ' ,
style: fontTextStyle (
12 , Color ( 0XFF4692FD ) , FontWeight . w500 ) ,
) ,
Text (
SuppliersList [ index ] . matchedPrice . toString ( ) ! = " null " & & SuppliersList [ index ] . matchedPrice . toString ( ) ! = " "
" " ? ' \u20B9 ${ AppSettings . formDouble ( SuppliersList [ index ] . matchedPrice . toString ( ) ) } ' : ' ' ,
style: fontTextStyle ( 10 , Color ( 0XFF515253 ) , FontWeight . w400 ) ,
) ,
] ,
) ,
SizedBox (
height:
MediaQuery . of ( context ) . size . height * . 004 ,
) ,
/// Address and Favourite
Row (
mainAxisAlignment:
MainAxisAlignment . spaceBetween ,
children: [
Expanded (
child: Text (
' ${ SuppliersList [ index ] . displayAddress } ${ SuppliersList [ index ] . distanceInMeters } Km ' ,
style: fontTextStyle ( 12 ,
Color ( 0XFF515253 ) , FontWeight . w400 ) ,
overflow: TextOverflow . ellipsis ,
) ,
) ,
GestureDetector (
onTap: ( ) async {
AppSettings . preLoaderDialog ( context ) ;
if ( SuppliersList [ index ] . isFavorite ) {
try {
bool tankerResponse =
await AppSettings
. removeFavourites (
SuppliersList [ index ]
. supplier_id ) ;
if ( tankerResponse ) {
Navigator . of ( context ,
rootNavigator: true )
. pop ( ) ;
AppSettings . longSuccessToast (
' Supplier removed from favourites ' ) ;
await getAllSuppliers ( ) ;
// await getConnectedSuppliersData();
} else {
Navigator . of ( context ,
rootNavigator: true )
. pop ( ) ;
AppSettings . longFailedToast (
' Failed to remove from favourites ' ) ;
}
} catch ( e ) {
Navigator . of ( context ,
rootNavigator: true )
. pop ( ) ;
AppSettings . longFailedToast (
' Failed to remove from favourites ' ) ;
}
} else {
try {
bool tankerResponse =
await AppSettings . addFavourites (
SuppliersList [ index ]
. supplier_id ) ;
if ( tankerResponse ) {
Navigator . of ( context ,
rootNavigator: true )
. pop ( ) ;
AppSettings . longSuccessToast (
' Supplier added to favourites ' ) ;
await getAllSuppliers ( ) ;
//await getConnectedSuppliersData();
//await getAllFavouritesData();
} else {
Navigator . of ( context ,
rootNavigator: true )
. pop ( ) ;
AppSettings . longFailedToast (
' Failed to add favourites ' ) ;
}
} catch ( e ) {
Navigator . of ( context ,
rootNavigator: true )
. pop ( ) ;
AppSettings . longFailedToast (
' Failed to add favourites ' ) ;
}
}
} ,
child: Image . asset (
SuppliersList [ index ] . isFavorite
? ' images/heart_active.png '
: ' images/heart_outline.png ' ,
width: 20 ,
height: 20 ,
) ,
) ,
] ,
) ,
] ,
) ,
) ,
] ,
) ,
SizedBox (
height: MediaQuery . of ( context ) . size . height * . 016 ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
Expanded (
child: GestureDetector (
onTap: ( ) { } ,
child: Container (
decoration: BoxDecoration (
shape: BoxShape . rectangle ,
color: Color ( 0XFFFFFFFF ) ,
border: Border . all (
width: 1 ,
color: SuppliersList [ index ] . isRequetsedBooking ? Color ( 0XFF1D7AFC ) : Color ( 0XFF939495 ) ,
) ,
borderRadius: BorderRadius . circular (
24 ,
) ) ,
alignment: Alignment . center ,
child: Padding (
padding: EdgeInsets . fromLTRB ( 16 , 12 , 16 , 12 ) ,
child: Text ( ' Call ' ,
style: fontTextStyle ( 12 , SuppliersList [ index ] . isRequetsedBooking ? Color ( 0XFF1D7AFC ) : Color ( 0XFF939495 ) ,
FontWeight . w600 ) ) ,
) ,
) ,
) ,
) ,
SizedBox (
width: MediaQuery . of ( context ) . size . width * . 010 ,
) ,
Expanded (
child: GestureDetector (
onTap: ( ) async {
if ( ! SuppliersList [ index ] . isRequetsedBooking ) {
requestOrderDialog ( SuppliersList [ index ] ) ;
}
/ * Navigator . push (
context ,
MaterialPageRoute (
builder: ( context ) = > PlaceOrder ( details: connectedSuppliersList [ index ] , ) ) ,
) ; * /
} ,
child: Container (
decoration: BoxDecoration (
shape: BoxShape . rectangle ,
color: SuppliersList [ index ] . isRequetsedBooking ? Color ( 0XFFDBDBDC ) : Color ( 0XFF1D7AFC ) ,
border: Border . all (
width: 1 ,
color: Color ( 0XFFFFFFFF ) ,
) ,
borderRadius: BorderRadius . circular (
24 ,
) ) ,
alignment: Alignment . center , //
child: Padding (
padding: EdgeInsets . fromLTRB ( 16 , 12 , 16 , 12 ) ,
child: Text ( ' Request Order ' ,
style: fontTextStyle (
12 , Color ( 0XFFFFFFFF ) , FontWeight . w600 ) ) ,
) ,
) ,
) )
] ,
)
] ,
) ,
) ,
) ,
)
;
} ,
)
] ,
) ,
) ;
}
requestOrderDialog ( var details ) {
return showDialog (
context: context ,
barrierDismissible: false ,
builder: ( BuildContext context ) {
return StatefulBuilder (
builder: ( BuildContext context , StateSetter setState ) {
return AlertDialog (
backgroundColor: Color ( 0XFFFFFFFF ) , // Set your desired background color
shape: RoundedRectangleBorder (
borderRadius: BorderRadius . circular ( 12 ) , // Optional: Rounded corners
) ,
title: Center ( child: Column (
children: [
Align (
alignment: Alignment . centerLeft ,
child: Text ( ' ORDER DETAILS ' , style: fontTextStyle ( 16 , Color ( 0XFF646566 ) , FontWeight . w400 ) ) ,
) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 024 , ) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
Text ( ' Water Type ' , style: fontTextStyle ( 14 , Color ( 0XFF646566 ) , FontWeight . w500 ) ) ,
Text ( selectedWaterType ! , style: fontTextStyle ( 14 , Color ( 0XFF2D2E30 ) , FontWeight . w500 ) ) ,
] ,
) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 010 , ) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
Text ( ' Capacity ' , style: fontTextStyle ( 14 , Color ( 0XFF646566 ) , FontWeight . w500 ) ) ,
Text ( selectedCapacity ! , style: fontTextStyle ( 14 , Color ( 0XFF2D2E30 ) , FontWeight . w500 ) ) ,
] ,
) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 010 , ) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
Text ( ' Quantity ' , style: fontTextStyle ( 14 , Color ( 0XFF646566 ) , FontWeight . w500 ) ) ,
Text ( selectedQuantity ! , style: fontTextStyle ( 14 , Color ( 0XFF2D2E30 ) , FontWeight . w500 ) ) ,
] ,
) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 010 , ) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
Text ( ' Date ' , style: fontTextStyle ( 14 , Color ( 0XFF646566 ) , FontWeight . w500 ) ) ,
Text ( DateFormat ( ' dd-MMM-yyyy ' ) . format ( selectedDate ! ) , style: fontTextStyle ( 14 , Color ( 0XFF2D2E30 ) , FontWeight . w500 ) ) ,
] ,
) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 010 , ) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
Text ( ' Time ' , style: fontTextStyle ( 14 , Color ( 0XFF646566 ) , FontWeight . w500 ) ) ,
Text ( _timeRangeText , style: fontTextStyle ( 14 , Color ( 0XFF2D2E30 ) , FontWeight . w500 ) ) ,
] ,
) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 010 , ) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
Text ( ' Actual Price ' , style: fontTextStyle ( 14 , Color ( 0XFF646566 ) , FontWeight . w500 ) ) ,
Text (
details . matchedPrice . toString ( ) ! = " null " & & details . matchedPrice . toString ( ) ! = " "
" " ? ' \u20B9 ${ AppSettings . formDouble ( details . matchedPrice . toString ( ) ) } ' : ' ' ,
style: fontTextStyle ( 14 , Color ( 0XFF2D2E30 ) , FontWeight . w500 ) ,
) ,
] ,
) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 010 , ) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
Text ( ' Your Price ' , style: fontTextStyle ( 14 , Color ( 0XFF646566 ) , FontWeight . w500 ) ) ,
SizedBox (
width: MediaQuery . of ( context ) . size . width * 0.4 , // Adjust width as needed
child: TextFormField (
controller: _quotedAmountController ,
keyboardType: TextInputType . number ,
textCapitalization: TextCapitalization . sentences ,
maxLength: 10 ,
decoration: textFormFieldDecoration ( Icons . phone , ' ' ) ,
style: fontTextStyle ( 14 , Color ( 0XFF2D2E30 ) , FontWeight . w500 ) ,
cursorColor: Color ( 0XFF1D7AFC ) ,
//TextStyle(color: Colors.black,fontWeight: FontWeight.bold),
)
) ,
] ,
) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 010 , ) ,
Text ( ' A standard 10% advance is payable before order confirmation. The advance payment is negotiable. ' , style: italicFontTextStyle ( 12 , Color ( 0XFF515253 ) , FontWeight . w400 ) ) ,
] ,
) , ) ,
actions: < Widget > [
Center (
child: Row (
children: [
Expanded (
child: GestureDetector (
onTap: ( ) {
Navigator . of ( context ) . pop ( ) ;
} ,
child: Container (
decoration: BoxDecoration (
color: Colors . white ,
border: Border . all ( color: Color ( 0XFF1D7AFC ) , width: 1 ) ,
borderRadius: BorderRadius . circular ( 24 ) ,
) ,
alignment: Alignment . center ,
child: Padding (
padding: EdgeInsets . symmetric ( vertical: 12 ) ,
child: Text (
' Cancel ' ,
style: fontTextStyle ( 14 , Color ( 0XFF1D7AFC ) , FontWeight . w600 ) ,
) ,
) ,
) ,
) ,
) ,
SizedBox ( width: MediaQuery . of ( context ) . size . width * . 010 , ) ,
Expanded (
child: GestureDetector (
onTap: ( ) async {
final now = DateTime . now ( ) ;
List < DateTime > validTimes = await _getValidRequestTimes ( ) ;
if ( validTimes . length > = maxRequests ) {
AppSettings . longFailedToast (
" Maximum 5 requests allowed in any 15-minute window. " ) ;
return ;
}
final text = _quotedAmountController . text . trim ( ) ;
if ( text . isEmpty ) return ;
final quoted = int . tryParse ( text ) ;
if ( quoted = = null ) {
AppSettings . longFailedToast ( " Invalid number " ) ;
return ;
}
// ✅ SAFE matched price parsing
final matchedPriceStr = details . matchedPrice ? ? ' ' ;
final matched = int . tryParse ( matchedPriceStr ) ;
if ( matched = = null ) {
AppSettings . longFailedToast ( " Invalid matched price " ) ;
return ;
}
if ( quoted > matched + 300 | | quoted < matched - 300 ) {
AppSettings . longFailedToast (
" Enter ±300 within matched price $ matched " ) ;
return ;
}
// ✅ SAFE required values
final supplierId = details . supplier_id ? ? ' ' ;
if ( supplierId . isEmpty ) {
AppSettings . longFailedToast ( " Supplier not available " ) ;
return ;
}
if ( selectedDate = = null ) {
AppSettings . longFailedToast ( " Please select date " ) ;
return ;
}
AppSettings . preLoaderDialog ( context ) ;
bool isOnline = await AppSettings . internetConnectivity ( ) ;
if ( ! isOnline ) {
Navigator . of ( context , rootNavigator: true ) . pop ( ) ;
AppSettings . longFailedToast ( " Check your internet connection. " ) ;
return ;
}
// ✅ NULL-SAFE PAYLOAD
var payload = {
" customerId " : AppSettings . customerId ? ? ' ' ,
" type_of_water " : selectedWaterType ? ? ' ' ,
" capacity " : selectedCapacity ? ? ' ' ,
" quantity " : selectedQuantity ? ? ' ' ,
" date " : DateFormat ( ' dd-MMM-yyyy ' ) . format ( selectedDate ! ) ,
" time " : _timeRangeText ? ? ' ' ,
" requested_suppliers " : [
{
" supplierId " : supplierId ,
" quoted_amount " : quoted ,
" time " : DateFormat ( ' dd-MM-yyyy HH:mm ' ) . format ( now ) ,
}
] ,
} ;
debugPrint ( " PAYLOAD → $ payload " ) ;
bool status = await AppSettings . requestOrder ( payload ) ;
Navigator . of ( context , rootNavigator: true ) . pop ( ) ;
if ( status ) {
_quotedAmountController . clear ( ) ;
Navigator . pop ( context ) ;
validTimes . add ( now ) ;
validTimes . sort ( ) ;
await _saveRequestTimes ( validTimes ) ;
sentRequests = validTimes . length ;
if ( validTimes . isNotEmpty ) {
final firstExpiry =
validTimes . first . add ( const Duration ( minutes: 15 ) ) ;
final remaining =
firstExpiry . difference ( DateTime . now ( ) ) . inSeconds ;
if ( remaining > 0 ) _startCountdownTimer ( remaining ) ;
}
setState ( ( ) { } ) ;
await getAllSuppliers ( ) ;
AppSettings . longSuccessToast ( " Request Sent " ) ;
} else {
AppSettings . longFailedToast ( " Request sending failed " ) ;
}
} ,
child: Container (
decoration: BoxDecoration (
color: const Color ( 0XFF1D7AFC ) ,
border: Border . all ( color: const Color ( 0XFF1D7AFC ) , width: 1 ) ,
borderRadius: BorderRadius . circular ( 24 ) ,
) ,
alignment: Alignment . center ,
child: Padding (
padding: const EdgeInsets . symmetric ( vertical: 12 ) ,
child: Text (
' Send Request ' ,
style: fontTextStyle ( 14 , Colors . white , FontWeight . w600 ) ,
) ,
) ,
) ,
) ,
)
] ,
) ,
) ,
] ,
) ;
} ) ;
} ) ;
}
Future < void > getAllSuppliers ( ) async {
AppSettings . preLoaderDialog ( context ) ;
bool isOnline = await AppSettings . internetConnectivity ( ) ;
if ( isOnline ) {
var payload = new Map < String , dynamic > ( ) ;
payload [ " type_of_water " ] = selectedWaterType . toString ( ) ;
payload [ " capacity " ] = selectedCapacity . toString ( ) ;
payload [ " quantity " ] = selectedQuantity . toString ( ) ;
payload [ " date " ] = DateFormat ( ' dd-MMM-yyyy ' ) . format ( selectedDate ! ) ;
payload [ " time " ] = _timeRangeText . toString ( ) ;
payload [ " radius_from " ] = selectedRadius ? . start . round ( ) . toString ( ) ? ? " " ;
payload [ " radius_to " ] = selectedRadius ? . end . round ( ) . toString ( ) ? ? " " ;
payload [ " rating_to " ] = selectedRating ? . end . toString ( ) ? ? " " ;
payload [ " price_from " ] = selectedPrice ? . start . round ( ) . toString ( ) ? ? " " ;
payload [ " price_to " ] = selectedPrice ? . end . round ( ) . toString ( ) ? ? " " ;
payload [ " pump " ] = selectedPump . toString ( ) ;
setState ( ( ) {
isDataLoading = true ;
} ) ;
try {
var response = await AppSettings . getSuppliersForBooking ( payload ) ;
setState ( ( ) {
SuppliersList =
( ( jsonDecode ( response ) [ ' suppliers ' ] ) as List )
. map ( ( dynamic model ) {
return SuppliersModel . fromJson ( model ) ;
} ) . toList ( ) ;
// Clean the selected value: remove comma and " L"
String normalizedSelectedLitres = selectedCapacity ! . replaceAll ( " , " , " " ) . replaceAll ( " L " , " " ) . trim ( ) ;
List < dynamic > suppliersJson = jsonDecode ( response ) [ ' suppliers ' ] ;
SuppliersList = suppliersJson . map ( ( supplierData ) {
List < dynamic > tankers = supplierData [ ' tankers ' ] ;
String ? matchedPrice ;
for ( var tanker in tankers ) {
String capacity = tanker [ ' capacity ' ] . replaceAll ( " , " , " " ) . trim ( ) ;
if ( capacity = = normalizedSelectedLitres ) {
matchedPrice = tanker [ ' price ' ] ;
break ;
}
}
// Optional: attach matchedPrice to model if supported
var supplierModel = SuppliersModel . fromJson ( supplierData ) ;
supplierModel . matchedPrice = matchedPrice ; // <- Add this field to your model class
return supplierModel ;
} ) . toList ( ) ;
isDataLoading = false ;
Navigator . of ( context , rootNavigator: true ) . pop ( ) ;
} ) ;
} catch ( e ) {
setState ( ( ) {
isDataLoading = false ;
Navigator . of ( context , rootNavigator: true ) . pop ( ) ;
} ) ;
}
} else {
AppSettings . longFailedToast ( ' Please check internet connection ' ) ;
}
}
@ override
Widget build ( BuildContext context ) {
return Scaffold (
backgroundColor: Colors . white ,
appBar: AppBar (
backgroundColor: Color ( 0XFFFFFFFF ) ,
elevation: 0 ,
scrolledUnderElevation: 0 ,
titleSpacing: 0 ,
title: GestureDetector (
onTap: ( ) {
_showLocationBottomSheet ( ) ;
} ,
child: Column (
crossAxisAlignment: CrossAxisAlignment . start ,
mainAxisAlignment: MainAxisAlignment . start ,
children: [
Row (
mainAxisSize:
MainAxisSize . min , // Ensures it doesn't take extra space
children: [
Text (
' Home ' ,
style:
fontTextStyle ( 16 , Color ( 0XFF232527 ) , FontWeight . w600 ) ,
) ,
SizedBox ( width: 5 ) , // Space between text and icon
Image . asset (
' images/arrow_down.png ' , // Replace with your arrow image
width: 24 , // Adjust size
height: 24 ,
)
] ,
) ,
Text (
AppSettings . userAddress ,
style:
fontTextStyle ( 10 , Color ( 0XFF515253 ) , FontWeight . w400 ) ,
) ,
] ,
)
) ,
iconTheme: IconThemeData ( color: Color ( 0XFF2A2A2A ) ) ,
actions: [
Row (
children: [
Padding (
padding: EdgeInsets . fromLTRB ( 0 , 10 , 10 , 10 ) ,
child: IconButton (
icon: Image . asset (
' images/notification_appbar.png ' , // Example URL image
) ,
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 , // Adjust the fit
) ,
) ,
) ,
) ,
body: ListView (
padding: EdgeInsets . symmetric ( horizontal: 0 ) ,
children: [
Padding ( padding: EdgeInsets . fromLTRB ( 16 , 0 , 16 , 0 ) ,
child: Column (
children: [
! isSearchEnabled ? _beforeDataScreenAbove ( ) : Container ( ) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 024 ) ,
_buildDropdownField (
hint: " Type of Water " ,
value: selectedWaterType ,
items: waterTypes ,
onChanged: ( val ) = > setState ( ( ) = > selectedWaterType = val ) ,
) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 012 ) ,
Row (
children: [
Expanded (
child: _buildDropdownField (
hint: " Capacity " ,
value: selectedCapacity ,
items: capacities ,
onChanged: ( val ) = >
setState ( ( ) = > selectedCapacity = val ) ) ) ,
SizedBox ( width: MediaQuery . of ( context ) . size . width * . 024 ) ,
Expanded (
child: _buildDropdownField (
hint: " Quantity " ,
value: selectedQuantity ,
items: quantities ,
onChanged: ( val ) = >
setState ( ( ) = > selectedQuantity = val ) ) ) ,
] ,
) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 012 ) ,
Row (
children: [
Expanded (
child: _buildDatePickerField (
hint: " Date " ,
value: selectedDate ! = null
? DateFormat ( ' dd-MMM-yyyy ' ) . format ( selectedDate ! )
: null ,
onTap: ( ) = > _selectFromDate ( context ) ) ) ,
SizedBox ( width: MediaQuery . of ( context ) . size . width * . 024 ) ,
Expanded (
child: _buildTimePickerField (
hint: " Select Time " ,
value: _timeRangeText ! = ' ' ? _timeRangeText : null ,
onTap: ( ) = > selectedDate ! = null
? _selectTime ( context )
: ScaffoldMessenger . of ( context ) . showSnackBar (
SnackBar ( content: Text ( " Please select date first " ) ) ) ,
) ) ,
] ,
) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 024 ) ,
SizedBox (
width: double . infinity ,
height: 41 ,
child: ElevatedButton (
onPressed: ( ) async {
setState ( ( ) {
isSearchEnabled = true ;
} ) ;
if ( selectedWaterType ! = ' ' & &
selectedCapacity ! = ' ' & &
selectedDate ! = ' ' & &
_timeRangeText ! = ' ' & &
selectedQuantity ! = ' ' ) {
await getAllSuppliers ( ) ;
} else {
AppSettings . longFailedToast ( ' Please enter valid details ' ) ;
}
} ,
style: ElevatedButton . styleFrom (
backgroundColor: Color ( 0XFF1D7AFC ) ,
shape: RoundedRectangleBorder (
borderRadius: BorderRadius . circular ( 24 ) ,
) ,
) ,
child: Text (
" Search " ,
style: fontTextStyle ( 14 , Color ( 0xFFFFFFFF ) , FontWeight . w600 ) ,
) ,
) ,
) ,
SizedBox ( height: MediaQuery . of ( context ) . size . height * . 036 ) ,
] ,
) ,
) ,
! isSearchEnabled ? _beforeDataScreen ( ) : _suppliersDataScreen ( ) ,
] ,
) ,
) ;
}
Widget _buildDropdownField ( {
required String hint ,
required String ? value ,
required List < String > items ,
required ValueChanged < String ? > onChanged ,
} ) {
return DropdownButtonHideUnderline (
child: DropdownButton2 < String > (
isExpanded: true ,
hint: Padding (
padding: EdgeInsets . symmetric ( vertical: 0 ) ,
child: Text (
hint ,
style: fontTextStyle ( 14 , Color ( 0XFF939495 ) , FontWeight . w400 ) ,
) ,
) ,
value: value ,
items: items . map ( ( item ) {
return DropdownMenuItem < String > (
value: item ,
child: Padding (
padding: EdgeInsets . symmetric ( vertical: 4 ) ,
child: Text (
item ,
style: fontTextStyle ( 14 , Color ( 0xFF2D2E30 ) , FontWeight . w400 ) ,
) ,
) ,
) ;
} ) . toList ( ) ,
onChanged: onChanged ,
buttonStyleData: ButtonStyleData (
height: 48 ,
padding: EdgeInsets . symmetric ( horizontal: 12 ) ,
decoration: BoxDecoration (
border: Border . all ( color: Color ( 0XFF939495 ) ) ,
borderRadius: BorderRadius . circular ( 12 ) ,
) ,
) ,
iconStyleData: IconStyleData (
icon: Padding (
padding: EdgeInsets . only ( right: 4 ) , // Right spacing from edge
child: Image . asset (
' images/arrow_down.png ' ,
width: 16 ,
height: 16 ,
color: Color ( 0XFF939495 ) ,
) ,
) ,
) ,
dropdownStyleData: DropdownStyleData (
padding: EdgeInsets . zero ,
decoration: BoxDecoration (
color: Colors . white ,
borderRadius: BorderRadius . circular ( 12 ) ,
) ,
) ,
menuItemStyleData: MenuItemStyleData (
padding: EdgeInsets . symmetric ( horizontal: 12 , vertical: 4 ) ,
) ,
) ,
) ;
}
Widget _buildDatePickerField ( {
required String hint ,
String ? value ,
required VoidCallback onTap ,
} ) {
return GestureDetector (
onTap: onTap ,
child: Container (
height: 48 ,
padding: EdgeInsets . symmetric ( horizontal: 12 ) ,
decoration: BoxDecoration (
border: Border . all ( color: Colors . grey . shade400 ) ,
borderRadius: BorderRadius . circular ( 12 ) ,
) ,
child: Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
Text (
value ? ? hint ,
style: value ! = null
? fontTextStyle ( 14 , Color ( 0xFF2D2E30 ) , FontWeight . w400 )
: fontTextStyle ( 14 , Color ( 0XFF939495 ) , FontWeight . w400 ) ,
) ,
Image . asset (
' images/calender_supplier_landing.png ' ,
fit: BoxFit . cover ,
width: 16 , // Match the diameter of the CircleAvatar
height: 16 ,
color: Color ( 0XFF939495 ) ,
) ,
] ,
) ,
) ,
) ;
}
Widget _buildTimePickerField ( {
required String hint ,
String ? value ,
required VoidCallback onTap ,
} ) {
return GestureDetector (
onTap: onTap ,
child: Container (
height: 48 ,
padding: EdgeInsets . symmetric ( horizontal: 12 ) ,
decoration: BoxDecoration (
border: Border . all ( color: Color ( 0XFF939495 ) ) ,
borderRadius: BorderRadius . circular ( 12 ) ,
) ,
child: Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
Expanded (
child: SingleChildScrollView (
scrollDirection: Axis . horizontal ,
child: Text (
value ? ? hint ,
style: value ! = null
? fontTextStyle ( 14 , Color ( 0xFF2D2E30 ) , FontWeight . w400 )
: fontTextStyle ( 14 , Color ( 0XFF939495 ) , FontWeight . w400 ) ,
overflow: TextOverflow . visible ,
) ,
) ,
) ,
SizedBox ( width: MediaQuery . of ( context ) . size . width * . 008 ) ,
Image . asset (
' images/arrow_down.png ' ,
width: 16 ,
height: 16 ,
color: Color ( 0XFF939495 ) ,
) ,
] ,
) ,
) ,
) ;
}
}