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