diff --git a/images/Add_icon.png b/images/Add_icon.png new file mode 100644 index 0000000..c26cc46 Binary files /dev/null and b/images/Add_icon.png differ diff --git a/images/arrow-up.png b/images/arrow-up.png new file mode 100644 index 0000000..b0089a2 Binary files /dev/null and b/images/arrow-up.png differ diff --git a/images/calendar_check.png b/images/calendar_check.png new file mode 100644 index 0000000..e870607 Binary files /dev/null and b/images/calendar_check.png differ diff --git a/images/downarrow.png b/images/downarrow.png new file mode 100644 index 0000000..3d6fd82 Binary files /dev/null and b/images/downarrow.png differ diff --git a/images/flag.png b/images/flag.png new file mode 100644 index 0000000..9eda3bb Binary files /dev/null and b/images/flag.png differ diff --git a/images/google_maps.png b/images/google_maps.png new file mode 100644 index 0000000..ded5474 Binary files /dev/null and b/images/google_maps.png differ diff --git a/images/manage-users.png b/images/manage-users.png new file mode 100644 index 0000000..2440a99 Binary files /dev/null and b/images/manage-users.png differ diff --git a/images/marker-pin.png b/images/marker-pin.png new file mode 100644 index 0000000..8b6c779 Binary files /dev/null and b/images/marker-pin.png differ diff --git a/images/truck.png b/images/truck.png new file mode 100644 index 0000000..61c9941 Binary files /dev/null and b/images/truck.png differ diff --git a/lib/resources/availability.dart b/lib/resources/availability.dart new file mode 100644 index 0000000..78bbd20 --- /dev/null +++ b/lib/resources/availability.dart @@ -0,0 +1,352 @@ +import 'package:flutter/material.dart'; +import 'package:supplier_new/common/settings.dart'; +import 'package:supplier_new/resources/resources.dart'; +import 'package:supplier_new/resources/source_location2.dart'; + +void main() => runApp(const MaterialApp(home: AvailabilityScreen())); + +class AvailabilityScreen extends StatefulWidget { + const AvailabilityScreen({super.key}); + + @override + _AvailabilityScreenState createState() => _AvailabilityScreenState(); +} + +class _AvailabilityScreenState extends State { + final TextEditingController _advanceNoticeController = + TextEditingController(); + final TextEditingController _minDeliveriesController = + TextEditingController(); + final TextEditingController _phoneController = TextEditingController(); + TimeOfDay? _weekdayStartTime; + TimeOfDay? _weekdayEndTime; + TimeOfDay? _weekendStartTime; + TimeOfDay? _weekendEndTime; + + + final List _days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; + List _selectedDays = List.generate(7, (index) => false); + + Future _pickTime({required int slot}) async { + final TimeOfDay initialTime = TimeOfDay.now(); + final TimeOfDay? picked = + await showTimePicker(context: context, initialTime: initialTime); + if (picked == null) return; + setState(() { + switch (slot) { + case 0: + _weekdayStartTime = picked; + break; + case 1: + _weekdayEndTime = picked; + break; + case 2: + _weekendStartTime = picked; + break; + case 3: + _weekendEndTime = picked; + break; + } + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + backgroundColor: Colors.white, + surfaceTintColor: Colors.transparent, + elevation: 0, + scrolledUnderElevation: 0, + title: const Text("Complete Profile"), + actions: [ + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 10), + child: IconButton( + splashRadius: 20, + padding: EdgeInsets.zero, + icon: const Image( + image: AssetImage('images/calendar_appbar.png'), + width: 22, + height: 22, + ), + onPressed: () {}, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0, 10, 10, 10), + child: IconButton( + splashRadius: 20, + padding: EdgeInsets.zero, + icon: Image.asset('images/notification_appbar.png', + width: 22, height: 22), + onPressed: () {}, + ), + ), + ], + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Step indicator + Text('Step 5/5', + style: fontTextStyle(14, Colors.grey, FontWeight.normal)), + const SizedBox(height: 4), + Row( + children: List.generate(4, (index) { + return Expanded( + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 2), + height: 5, + decoration: BoxDecoration( + color: index < 4 ? const Color(0xFFC3C4C4) : Colors.grey, + borderRadius: BorderRadius.circular(2), + ), + ), + ); + }), + ), + const SizedBox(height: 20), + + // Availability + Text('AVAILABILITY', + style: fontTextStyle( + 16, const Color(0xFF2D2E30), FontWeight.w500)), + const SizedBox(height: 12), + Container( + width: 24, + height: 24, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('images/calendar_check.png'), + fit: BoxFit.contain), + ), + ), + const SizedBox(height: 5), + Text('Set your operating schedule', + style: fontTextStyle( + 14, const Color(0xFF939495), FontWeight.w500)), + const SizedBox(height: 10), + + Text('AVAILABILITY', + style: fontTextStyle( + 14, const Color(0xFF8270DB), FontWeight.w600)), + const SizedBox(height: 12), + Text('Advance Notice Required (hours) *', + style: fontTextStyle( + 12, const Color(0xFF2F3036), FontWeight.w600)), + const SizedBox(height: 5), + TextField( + controller: _advanceNoticeController, + keyboardType: TextInputType.number, + decoration: InputDecoration( + border: OutlineInputBorder(), + hintText: "1", + hintStyle: + fontTextStyle(14, const Color(0xFF939495), FontWeight.w400), + ), + ), + + const SizedBox(height: 10), + + // Minimum Deliveries + Text('Minimum Deliveries (per day) *', + style: fontTextStyle( + 12, const Color(0xFF2F3036), FontWeight.w600)), + const SizedBox(height: 5), + TextField( + controller: _minDeliveriesController, + keyboardType: TextInputType.number, + decoration: InputDecoration( + border: OutlineInputBorder(), + hintText: '10', + hintStyle: + fontTextStyle(14, const Color(0xFF939495), FontWeight.w400), + ), + ), + const SizedBox(height: 10), + + // Days of Availability + Text( + 'Days of Availability *', + style: + fontTextStyle(12, const Color(0xFF251525), FontWeight.w600), + ), + const SizedBox(height: 5), + // Place this inside your build() where you want the row of day boxes + // Place this where you want the row of day boxes + Theme( + data: Theme.of(context).copyWith( + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + visualDensity: VisualDensity.compact, + ), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: List.generate(_days.length, (index) { + final day = _days[index]; + final selected = _selectedDays[index]; + + return Padding( + padding: const EdgeInsets.only(right: 7.0), // gap 10px + child: GestureDetector( + onTap: () => setState( + () => _selectedDays[index] = !_selectedDays[index]), + onSecondaryTap: () {}, + // swallow right-click / context menu + child: SizedBox( + width: 44, // fixed width + height: 42, // fixed height + child: Container( + padding: const EdgeInsets.all(8), + // inner padding 8px + decoration: BoxDecoration( + color: selected + ? const Color(0xFF6F5FBA) + : Colors.white, + // selected background + borderRadius: BorderRadius.circular(9), + // radius 9px + border: Border.all( + color: const Color(0xFF6F5FBA), + width: 0.5, // border 0.5px + ), + ), + alignment: Alignment.center, + child: Text( + day, + textAlign: TextAlign.center, + style: fontTextStyle( + 12, + selected + ? Colors.white + : const Color(0xFF515253), + FontWeight.w600, + ), + ), + ), + ), + ), + ); + }), + ), + ), + ), + + const SizedBox(height: 10), + + // Phone Number + Text('Phone Number *', + style: fontTextStyle( + 12, const Color(0xFF2F3036), FontWeight.w600)), + const SizedBox(height: 5), + TextField( + controller: _phoneController, + keyboardType: TextInputType.phone, + decoration: InputDecoration( + border: OutlineInputBorder(), + hintText: '+91 | Mobile Number', + hintStyle: + fontTextStyle(14, const Color(0xFF939495), FontWeight.w400), + ), + ), + const SizedBox(height: 20), + + // Operating Hours + Text('Operating Hours', + style: fontTextStyle( + 14, const Color(0xFF8270DB), FontWeight.w600)), + const SizedBox(height: 5), + LayoutBuilder( + builder: (context, constraints) { + // compute width for 2 columns with 10px gap and 16px padding (adjust if needed) + final double totalGap = 10.0; + final double itemWidth = (constraints.maxWidth - totalGap) / 2; + + Widget buildTimeBox({ + required String label, + required TimeOfDay? time, + required int slotIndex, + }) { + return SizedBox( + width: itemWidth, + // smaller height than before + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(label, style: fontTextStyle(12, const Color(0xFF515253), FontWeight.w600)), + const SizedBox(height: 5), + GestureDetector( + onTap: () => _pickTime(slot: slotIndex), + child: Container( + height: 42, // smaller height + padding: const EdgeInsets.symmetric(horizontal: 10), + decoration: BoxDecoration( + border: Border.all(color: Colors.grey.shade400), + borderRadius: BorderRadius.circular(6), + ), + alignment: Alignment.centerLeft, + child: Text( + time != null ? time.format(context) : '--:--', + style: fontTextStyle(14, Colors.black, FontWeight.normal), + ), + ), + ), + ], + ), + ); + } + + return Column( + children: [ + Row( + children: [ + buildTimeBox(label: 'Weekday Start Time *', time: _weekdayStartTime, slotIndex: 0), + const SizedBox(width: 10), + buildTimeBox(label: 'Weekday End Time *', time: _weekdayEndTime, slotIndex: 1), + ], + ), + const SizedBox(height: 12), + Row( + children: [ + buildTimeBox(label: 'Weekend Start Time', time: _weekendStartTime, slotIndex: 2), + const SizedBox(width: 10), + buildTimeBox(label: 'Weekend End Time', time: _weekendEndTime, slotIndex: 3), + ], + ), + ], + ); + }, + ), + + const SizedBox(height: 12), + SizedBox( + width: double.infinity, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF8270DB), + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 14), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(24)), + ), + onPressed: () { + // TODO: Navigate to the next step/screen + Navigator.push(context, MaterialPageRoute(builder: (_) => const ResourcesScreen())); + + }, + child: Text( + " Save \$ Continue", + style: fontTextStyle(14, Colors.white, FontWeight.w400), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/resources/employees.dart b/lib/resources/employees.dart new file mode 100644 index 0000000..b5acede --- /dev/null +++ b/lib/resources/employees.dart @@ -0,0 +1,420 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:supplier_new/common/settings.dart'; +import 'package:supplier_new/resources/source_location.dart'; + +import 'Fleet_1.dart'; + +// If you want to navigate on Continue, import your next page here. +// import 'fleet_1.dart'; + +void main() => runApp(const MaterialApp(home: FleetEmployees())); + +class FleetEmployees extends StatefulWidget { + const FleetEmployees({super.key}); + + @override + State createState() => _FleetEmployeesState(); +} + +class _FleetEmployeesState extends State { + final _formKey = GlobalKey(); + + // Controllers + final _nameCtrl = TextEditingController(); + final _mobileCtrl = TextEditingController(); + final _altMobileCtrl = TextEditingController(); + + // Dropdowns + final List licenseNumbers = [ + "UP3220050012345", + "UP3220050012355", + "UP3220050012365", + "UP3220050012375", + ]; + String? selectedLicense; + + final List yearOptions = ["1", "2", "3", "4", "5"]; + String? selectedExperience; + + // Data bucket + final List> _drivers = []; + + // Validators + String? _required(String? v, {String field = "This field"}) { + if (v == null || v.trim().isEmpty) return "$field is required"; + return null; + } + + String? _validatePhone(String? v, {String label = "Phone Number"}) { + if (v == null || v.trim().isEmpty) return "$label is required"; + final digits = v.replaceAll(RegExp(r'\D'), ''); + if (digits.length != 10) return "Enter a 10-digit $label"; + if (!RegExp(r'^[6-9]\d{9}$').hasMatch(digits)) { + return "$label must start with 6/7/8/9"; + } + return null; + } + + @override + void dispose() { + _nameCtrl.dispose(); + _mobileCtrl.dispose(); + _altMobileCtrl.dispose(); + super.dispose(); + } + + Map _buildPayload() => { + "driver_name": _nameCtrl.text.trim(), + "license_number": selectedLicense, + "experience_years": selectedExperience, + "phone": _mobileCtrl.text.trim(), + "alt_phone": _altMobileCtrl.text.trim(), + }; + + void _clearForm() { + _nameCtrl.clear(); + _mobileCtrl.clear(); + _altMobileCtrl.clear(); + selectedLicense = null; + selectedExperience = null; + setState(() {}); + } + + void _addDriver() { + final ok = _formKey.currentState?.validate() ?? false; + setState(() {}); // ensure error texts render + if (!ok || + selectedLicense == null || + selectedExperience == null || + selectedLicense!.isEmpty || + selectedExperience!.isEmpty) { + if (selectedLicense == null || selectedExperience == null) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("Please select License & Experience")), + ); + } + return; + } + _drivers.add(_buildPayload()); + _clearForm(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Driver added (${_drivers.length})")), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + backgroundColor: Colors.white, + surfaceTintColor: Colors.transparent, + elevation: 0, + scrolledUnderElevation: 0, + title: const Text("Complete Profile"), + actions: [ + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 10), + child: IconButton( + splashRadius: 20, + padding: EdgeInsets.zero, + icon: const Image(image: AssetImage('images/calendar_appbar.png'), width: 22, height: 22), + onPressed: () {}, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0, 10, 10, 10), + child: IconButton( + splashRadius: 20, + padding: EdgeInsets.zero, + icon: Image.asset('images/notification_appbar.png', width: 22, height: 22), + onPressed: () {}, + ), + ), + ], + ), + body: SafeArea( + child: Form( + key: _formKey, + child: ListView( + padding: const EdgeInsets.fromLTRB(20, 10, 20, 24), + children: [ + // Step indicator + Text( + "Step 1/5", + style: fontTextStyle(16, const Color(0xFFC3C4C4), FontWeight.w500), + ), + const SizedBox(height: 16), + + // Header block + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("EMPLOYEES", style: fontTextStyle(20, const Color(0xFF515253), FontWeight.w600)), + const SizedBox(height: 8), + Container( + width: 24, + height: 24, + decoration: const BoxDecoration( + image: DecorationImage(image: AssetImage('images/manage-users.png'), fit: BoxFit.contain), + ), + ), + ], + ), + const SizedBox(height: 6), + Text( + "Details about your water tanker fleet", + style: fontTextStyle(14, const Color(0xFF939495), FontWeight.w500), + ), + const SizedBox(height: 16), + + // Section header (just the bar) + _SectionHeaderBar( + title: "DRIVER #1", + icon: Image.asset('images/arrow-up.png', width: 16, height: 16), + radius: 20, + ), + const SizedBox(height: 12), + + // === Fields + _LabeledField( + label: "Driver Name *", + child: TextFormField( + controller: _nameCtrl, + validator: (v) => _required(v, field: "Driver Name"), + decoration: const InputDecoration( + hintText: "Full Name", + border: OutlineInputBorder(), + isDense: true, + ), + textInputAction: TextInputAction.next, + ), + ), + + _LabeledField( + label: "Driver License Number *", + child: DropdownButtonFormField( + value: selectedLicense, + items: licenseNumbers + .map((t) => DropdownMenuItem(value: t, child: Text(t))) + .toList(), + onChanged: (v) => setState(() => selectedLicense = v), + validator: (v) => v == null || v.isEmpty ? "Driver License required" : null, + isExpanded: true, + alignment: Alignment.centerLeft, + hint: Text( + "Select License Number", + style: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400), + ), + icon: Image.asset('images/downarrow.png', width: 16, height: 16), + decoration: const InputDecoration( + border: OutlineInputBorder(), + isDense: false, + contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 14), + ), + ), + ), + + _LabeledField( + label: "Years of Experience *", + child: DropdownButtonFormField( + value: selectedExperience, + items: yearOptions + .map((t) => DropdownMenuItem(value: t, child: Text(t))) + .toList(), + onChanged: (v) => setState(() => selectedExperience = v), + validator: (v) => v == null || v.isEmpty ? "Experience is required" : null, + isExpanded: true, + alignment: Alignment.centerLeft, + hint: Text( + "Years", + style: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400), + ), + icon: Image.asset('images/downarrow.png', width: 16, height: 16), + decoration: const InputDecoration( + border: OutlineInputBorder(), + isDense: false, + contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 14), + ), + ), + ), + + _LabeledField( + label: "Phone Number *", + child: TextFormField( + controller: _mobileCtrl, + validator: (v) => _validatePhone(v, label: "Phone Number"), + keyboardType: TextInputType.phone, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + LengthLimitingTextInputFormatter(10), + ], + decoration: InputDecoration( + hintText: "Mobile Number", + hintStyle: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400), + border: const OutlineInputBorder(), + isDense: true, + ), + textInputAction: TextInputAction.next, + ), + ), + + _LabeledField( + label: "Alternate Phone Number", + child: TextFormField( + controller: _altMobileCtrl, + validator: (v) { + if (v == null || v.trim().isEmpty) return null; // optional + return _validatePhone(v, label: "Alternate Phone Number"); + }, + keyboardType: TextInputType.phone, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + LengthLimitingTextInputFormatter(10), + ], + decoration: InputDecoration( + hintText: "Mobile Number", + hintStyle: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400), + border: const OutlineInputBorder(), + isDense: true, + ), + textInputAction: TextInputAction.next, + ), + ), + + const SizedBox(height: 20), + + // Actions + Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SizedBox( + width: double.infinity, + child: OutlinedButton.icon( + onPressed: _addDriver, + icon: Image.asset('images/Add_icon.png', width: 16, height: 16), + label: Text( + "Add Driver", + style: fontTextStyle(14, const Color(0xFF646566), FontWeight.w600), + ), + ), + ), + const SizedBox(height: 12), + SizedBox( + width: double.infinity, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF8270DB), + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 14), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)), + ), + onPressed: () { + // TODO: Navigate to the next step/screen + Navigator.push(context, MaterialPageRoute(builder: (_) => const SourceLocation())); + // ScaffoldMessenger.of(context).showSnackBar( + // SnackBar(content: Text("Saved ${_drivers.length} driver(s). Proceeding…")), + // ); + }, + child: Text( + "Continue", + style: fontTextStyle(14, Colors.white, FontWeight.w400), + ), + ), + ), + ], + ), + ], + ), + ), + ), + ); + } +} + +// ======= UI helpers ======= + +class _SectionHeaderBar extends StatelessWidget { + final String title; + final Widget? icon; + final Color backgroundColor; + final Color borderColor; + final double radius; + + const _SectionHeaderBar({ + required this.title, + this.icon, + this.backgroundColor = const Color(0xFFEEEEEE), + this.borderColor = const Color(0xFFE5E7EB), + this.radius = 12, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: backgroundColor, + border: Border.all(color: borderColor, width: 1), + borderRadius: BorderRadius.circular(radius), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.04), + blurRadius: 6, + offset: const Offset(0, 2), + ), + ], + ), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + child: Row( + children: [ + Expanded( + child: Text( + title, + style: fontTextStyle(12, const Color(0xFF2D2E30), FontWeight.w600), + ), + ), + if (icon != null) icon!, + ], + ), + ); + } +} + +class _LabeledField extends StatelessWidget { + final String label; + final Widget child; + final String? Function()? validator; // (kept from your earlier helper; not used here) + + const _LabeledField({ + required this.label, + required this.child, + this.validator, + }); + + @override + Widget build(BuildContext context) { + final errorText = validator != null ? validator!() : null; + + return Padding( + padding: const EdgeInsets.only(bottom: 14.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(label, style: fontTextStyle(12, const Color(0xFF515253), FontWeight.w600)), + const SizedBox(height: 6), + child, + if (errorText != null) + Padding( + padding: const EdgeInsets.only(top: 6), + child: Text( + errorText, + style: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400), + ), + ), + ], + ), + ); + } +} diff --git a/lib/resources/fleet.dart b/lib/resources/fleet.dart index f8e25ba..6bcb8a8 100644 --- a/lib/resources/fleet.dart +++ b/lib/resources/fleet.dart @@ -1,5 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:supplier_new/common/settings.dart'; + +import 'employees.dart'; +import 'fleet_1.dart'; void main() => runApp(const MaterialApp(home: FleetStep1Page())); @@ -15,8 +19,8 @@ class _FleetStep1PageState extends State { // Controllers final _nameCtrl = TextEditingController(); - final _capacityCtrl = TextEditingController(text: "10,000"); // hint-like - final _plateCtrl = TextEditingController(text: "AB 05 H 4948"); + final _capacityCtrl = TextEditingController(); // hint-like + final _plateCtrl = TextEditingController(); final _mfgYearCtrl = TextEditingController(); final _insExpiryCtrl = TextEditingController(); @@ -50,9 +54,8 @@ class _FleetStep1PageState extends State { helpText: "Select Insurance Expiry Date", ); if (date != null) { - _insExpiryCtrl.text = "${date.year.toString().padLeft(4, '0')}-" - "${date.month.toString().padLeft(2, '0')}-" - "${date.day.toString().padLeft(2, '0')}"; + _insExpiryCtrl.text = + "${date.year.toString().padLeft(4, '0')}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}"; setState(() {}); } } @@ -72,24 +75,80 @@ class _FleetStep1PageState extends State { super.dispose(); } + // Store multiple tanker entries + final List> _tankers = []; + + Map _buildPayload() => { + "tanker_name": _nameCtrl.text.trim(), + "capacity": _capacityCtrl.text.trim(), + "type": selectedType, + "features": selectedFeatures.toList(), + "license_plate": _plateCtrl.text.trim(), + "manufacturing_year": _mfgYearCtrl.text.trim(), + "insurance_expiry": _insExpiryCtrl.text.trim(), + }; + + void _clearForm() { + _nameCtrl.clear(); + _capacityCtrl.text = "10,000"; + _plateCtrl.text = "AB 05 H 4948"; + _mfgYearCtrl.clear(); + _insExpiryCtrl.clear(); + selectedType = null; + selectedFeatures.clear(); + setState(() {}); + } + + void _addTanker() { + final ok = _formKey.currentState?.validate() ?? false; + setState(() {}); // in case you show chip validation below labels + if (!ok || selectedFeatures.isEmpty) { + if (selectedFeatures.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("Select at least one Tanker Feature")), + ); + } + return; + } + _tankers.add(_buildPayload()); + _clearForm(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Tanker added (${_tankers.length})")), + ); + } + @override Widget build(BuildContext context) { final theme = Theme.of(context); - final grey = Colors.grey.shade600; return Scaffold( + backgroundColor: Colors.white, appBar: AppBar( + backgroundColor: Colors.white, title: const Text("Complete Profile"), - centerTitle: false, - actions: const [ - Padding( - padding: EdgeInsets.only(right: 12), - child: Icon(Icons.calendar_today_outlined), - ), - Padding( - padding: EdgeInsets.only(right: 12), - child: Icon(Icons.more_vert), + actions: [ + Padding(padding: EdgeInsets.fromLTRB(10,10,0,10), + child: IconButton( + icon: Image( + image: AssetImage('images/calendar_appbar.png') + + ), + onPressed: (){ + + }, + ), ), + + Padding(padding: EdgeInsets.fromLTRB(0,10,10,10), + child: IconButton( + icon: Image.asset( + 'images/notification_appbar.png', // Example URL image + ), + onPressed: (){ + + }, + ), + ) ], ), body: SafeArea( @@ -98,146 +157,248 @@ class _FleetStep1PageState extends State { child: ListView( padding: const EdgeInsets.fromLTRB(20, 10, 20, 24), children: [ - Text("Step 1/5", style: theme.textTheme.labelLarge?.copyWith(color: grey)), + // Step indicator + Text( + "Step 1/5", + style: + fontTextStyle(16, const Color(0xFFC3C4C4), FontWeight.w500), + ), const SizedBox(height: 16), - // FLEET header - Row( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, children: [ - Icon(Icons.local_shipping_outlined, color: Colors.deepPurple.shade400), - const SizedBox(width: 8), - Text("FLEET", style: theme.textTheme.titleMedium?.copyWith(letterSpacing: 1.2)), + Text( + "FLEET", + style: fontTextStyle( + 20, const Color(0xFF515253), FontWeight.w600), + ), + const SizedBox(height: 8), + Container( + width: 24, + height: 24, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('images/truck.png'), + fit: BoxFit.contain, + ), + ), + ), ], ), + const SizedBox(height: 6), Text( "Details about your water tanker fleet", - style: theme.textTheme.bodySmall?.copyWith(color: grey), + style: + fontTextStyle(14, const Color(0xFF939495), FontWeight.w500), ), const SizedBox(height: 16), - // Water Tanker card - _SectionCard( + _SectionHeaderBar( title: "WATER TANKER #1", - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _LabeledField( - label: "Tanker Name *", - child: TextFormField( - controller: _nameCtrl, - decoration: const InputDecoration( - hintText: "Enter Tanker Name", - ), - validator: (v) => _required(v, field: "Tanker Name"), - ), - ), - _LabeledField( - label: "Tanker Capacity (in L) *", - child: TextFormField( - controller: _capacityCtrl, - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'[0-9,]')), - ], - decoration: const InputDecoration( - hintText: "10,000", - suffixText: "L", - ), - validator: (v) => _required(v, field: "Capacity"), - ), - ), - _LabeledField( - label: "Tanker Type *", - child: DropdownButtonFormField( - value: selectedType, - items: tankerTypes - .map((e) => DropdownMenuItem(value: e, child: Text(e))) - .toList(), - onChanged: (v) => setState(() => selectedType = v), - decoration: const InputDecoration(hintText: "Select Type"), - validator: (v) => v == null ? "Tanker Type is required" : null, - ), - ), - _LabeledField( - label: "Tanker Features *", - child: _MultiSelectChips( - options: featureOptions, - selected: selectedFeatures, - onChanged: (s) => setState(() => selectedFeatures - ..clear() - ..addAll(s)), - ), - validator: () => - selectedFeatures.isEmpty ? "Select at least one feature" : null, - ), - _LabeledField( - label: "License Plate *", - child: TextFormField( - controller: _plateCtrl, - textCapitalization: TextCapitalization.characters, - decoration: const InputDecoration(hintText: "AB 05 H 4948"), - validator: (v) => _required(v, field: "License Plate"), - ), - ), - _LabeledField( - label: "Manufacturing Year (opt)", - child: TextFormField( - controller: _mfgYearCtrl, - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - LengthLimitingTextInputFormatter(4), - ], - decoration: const InputDecoration(hintText: "YYYY"), - validator: (_) => null, // optional + icon: Image.asset('images/arrow-up.png', width: 16, height: 16), + radius: 20, + ), + const SizedBox(height: 12), + + _LabeledField( + label: "Tanker Name *", + child: TextFormField( + controller: _nameCtrl, + validator: (v) => _required(v, field: "Tanker Name"), + decoration: const InputDecoration( + hintText: "Enter Tanker Name", + border: OutlineInputBorder(), + isDense: true, + ), + textInputAction: TextInputAction.next, + ), + ), + + _LabeledField( + label: "Tanker Capacity (in L) *", + child: TextFormField( + controller: _capacityCtrl, + validator: (v) => _required(v, field: "Tanker Capacity"), + decoration: InputDecoration( + hintText: "10,000 L", + hintStyle: fontTextStyle( + 14, const Color(0xFF939495), FontWeight.w400), + border: const OutlineInputBorder(), + isDense: true, + ), + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[0-9,]')), + ], + textInputAction: TextInputAction.next, + ), + ), + + _LabeledField( + label: "Tanker Type *", + child: DropdownButtonFormField( + value: selectedType, + items: tankerTypes + .map((t) => DropdownMenuItem(value: t, child: Text(t))) + .toList(), + onChanged: (v) => setState(() => selectedType = v), + validator: (v) => + v == null || v.isEmpty ? "Tanker Type is required" : null, + isExpanded: true, + alignment: Alignment.centerLeft, + hint: Text( + "Select Type", + style: fontTextStyle( + 14, const Color(0xFF939495), FontWeight.w400), + ), + + icon: Image.asset('images/downarrow.png', + width: 16, height: 16), + + decoration: const InputDecoration( + border: OutlineInputBorder(), + isDense: false, + contentPadding: + EdgeInsets.symmetric(horizontal: 12, vertical: 14), + ), + ), + ), + + _LabeledField( + label: "Tanker Features *", + child: DropdownButtonFormField( + value: selectedType, + items: tankerTypes + .map((t) => DropdownMenuItem(value: t, child: Text(t))) + .toList(), + onChanged: (v) => setState(() => selectedType = v), + validator: (v) => + v == null || v.isEmpty ? "Tanker Type is required" : null, + isExpanded: true, + alignment: Alignment.centerLeft, + + // <-- Hint: left-aligned, vertically centered by padding + hint: Text( + "Select Features", + style: fontTextStyle( + 14, const Color(0xFF939495), FontWeight.w400), + ), + + icon: Image.asset('images/downarrow.png', + width: 16, height: 16), + + decoration: const InputDecoration( + border: OutlineInputBorder(), + isDense: false, // keep some height + contentPadding: + EdgeInsets.symmetric(horizontal: 12, vertical: 14), + ), + ), + ), + + _LabeledField( + label: "License Plate *", + child: TextFormField( + controller: _plateCtrl, + validator: (v) => _required(v, field: "License Plate"), + decoration: InputDecoration( + hintText: "AB 05 H 4948", + hintStyle: fontTextStyle( + 14, const Color(0xFF939495), FontWeight.w400), + border: const OutlineInputBorder(), + isDense: true, + ), + textCapitalization: TextCapitalization.characters, + textInputAction: TextInputAction.next, + ), + ), + + _LabeledField( + label: "Manufacturing Year (opt)", + child: TextFormField( + controller: _mfgYearCtrl, + decoration: InputDecoration( + hintText: "YYYY", + hintStyle: fontTextStyle( + 14, const Color(0xFF939495), FontWeight.w400), + border: const OutlineInputBorder(), + isDense: true, + ), + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + LengthLimitingTextInputFormatter(4), + ], + textInputAction: TextInputAction.next, + ), + ), + + _LabeledField( + label: "Insurance Expiry Date (opt)", + child: TextFormField( + controller: _insExpiryCtrl, + readOnly: true, + decoration: InputDecoration( + hintText: "DD-MM-YYYY", + hintStyle: fontTextStyle( + 14, const Color(0xFF939495), FontWeight.w400), + border: const OutlineInputBorder(), + isDense: true, + ), + onTap: _pickInsuranceDate, + ), + ), + + const SizedBox(height: 20), + // Save / Continue + Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SizedBox( + width: double.infinity, + child: OutlinedButton.icon( + onPressed: _addTanker, + icon: Image.asset('images/Add_icon.png', + width: 16, height: 16), + // pulsing image + label: Text( + "Add Tanker", + style: fontTextStyle( + 14, const Color(0xFF646566), FontWeight.w600), ), ), - _LabeledField( - label: "Insurance Expiry Date (opt)", - child: TextFormField( - controller: _insExpiryCtrl, - readOnly: true, - onTap: _pickInsuranceDate, - decoration: const InputDecoration( - hintText: "Select date", - suffixIcon: Icon(Icons.calendar_month_outlined), - ), + ), + + const SizedBox(height: 12), + + // Continue (primary) + SizedBox( + width: double.infinity, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF8270DB), // purple bg + foregroundColor: Colors.white, // ripple/icon/text color + padding: const EdgeInsets.symmetric(vertical: 14), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(24)), ), - ), - const SizedBox(height: 8), - FilledButton( onPressed: () { - // manual validation for multi-select too - final chipsValid = selectedFeatures.isNotEmpty; - final valid = _formKey.currentState!.validate() && chipsValid; - setState(() {}); // to refresh potential helper text in chips - if (!valid) return; - - // Collect values - final data = { - "tanker_name": _nameCtrl.text.trim(), - "capacity_liters": _capacityCtrl.text.replaceAll(",", "").trim(), - "tanker_type": selectedType, - "features": selectedFeatures.toList(), - "license_plate": _plateCtrl.text.trim(), - "manufacturing_year": _mfgYearCtrl.text.trim().isEmpty - ? null - : _mfgYearCtrl.text.trim(), - "insurance_expiry": _insExpiryCtrl.text.trim().isEmpty - ? null - : _insExpiryCtrl.text.trim(), - }; - - // TODO: send to backend - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text("Saved: $data")), + Navigator.push( + context, + MaterialPageRoute(builder: (_) => const FleetStep2Page()), ); }, - child: const Text("Save & Continue"), + child: Text( + "Continue", + style: fontTextStyle( + 14, Colors.white, FontWeight.w400), // white text + ), ), - ], - ), - ), + ) + ], + ) ], ), ), @@ -246,42 +407,50 @@ class _FleetStep1PageState extends State { } } -/// Section card with a folding-style title bar (static in this example) -class _SectionCard extends StatelessWidget { + +class _SectionHeaderBar extends StatelessWidget { final String title; - final Widget child; + final Widget? icon; + final Color backgroundColor; + final Color borderColor; + final double radius; - const _SectionCard({required this.title, required this.child}); + const _SectionHeaderBar({ + required this.title, + this.icon, + this.backgroundColor = const Color(0xFFEEEEEE), + this.borderColor = const Color(0xFFE5E7EB), + this.radius = 8, + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { - final border = RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)); - return Card( - elevation: 0.8, - shape: border, - margin: const EdgeInsets.only(top: 12), - child: Padding( - padding: const EdgeInsets.fromLTRB(12, 8, 12, 12), - child: Column( - children: [ - Row( - children: [ - Expanded( - child: Text( - title, - style: Theme.of(context) - .textTheme - .labelLarge - ?.copyWith(letterSpacing: .6, color: Colors.grey.shade700), - ), - ), - const Icon(Icons.expand_less, size: 18, color: Colors.grey), - ], + return Container( + decoration: BoxDecoration( + color: backgroundColor, + border: Border.all(color: borderColor, width: 1), + borderRadius: BorderRadius.circular(radius), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.04), + blurRadius: 6, + offset: const Offset(0, 2), + ), + ], + ), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + child: Row( + children: [ + Expanded( + child: Text( + title, + style: + fontTextStyle(12, const Color(0xFF2D2E30), FontWeight.w600), ), - const Divider(height: 20), - child, - ], - ), + ), + if (icon != null) icon!, + ], ), ); } @@ -300,11 +469,11 @@ class _LabeledField extends StatelessWidget { @override Widget build(BuildContext context) { - final labelStyle = - Theme.of(context).textTheme.bodyMedium?.copyWith(color: Colors.grey.shade800); + final labelStyle = Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(color: Colors.grey.shade800); - // If a custom validator is provided (e.g., for multi-select), - // show helper/error text below. final errorText = validator != null ? validator!() : null; return Padding( @@ -312,67 +481,22 @@ class _LabeledField extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(label, style: labelStyle), + Text(label, + style: + fontTextStyle(12, const Color(0xFF515253), FontWeight.w600)), const SizedBox(height: 6), child, if (errorText != null) Padding( padding: const EdgeInsets.only(top: 6), - child: Text(errorText, - style: TextStyle(color: Theme.of(context).colorScheme.error, fontSize: 12)), + child: Text( + errorText, + style: + fontTextStyle(14, const Color(0xFF939495), FontWeight.w400), + ), ), ], ), ); } } - -class _MultiSelectChips extends StatefulWidget { - final List options; - final Set selected; - final ValueChanged> onChanged; - - const _MultiSelectChips({ - required this.options, - required this.selected, - required this.onChanged, - }); - - @override - State<_MultiSelectChips> createState() => _MultiSelectChipsState(); -} - -class _MultiSelectChipsState extends State<_MultiSelectChips> { - late Set _local; - - @override - void initState() { - super.initState(); - _local = {...widget.selected}; - } - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 8, - runSpacing: -6, - children: [ - for (final opt in widget.options) - FilterChip( - label: Text(opt), - selected: _local.contains(opt), - onSelected: (v) { - setState(() { - if (v) { - _local.add(opt); - } else { - _local.remove(opt); - } - }); - widget.onChanged(_local); - }, - ), - ], - ); - } -} diff --git a/lib/resources/fleet_1.dart b/lib/resources/fleet_1.dart new file mode 100644 index 0000000..d3bf662 --- /dev/null +++ b/lib/resources/fleet_1.dart @@ -0,0 +1,213 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:supplier_new/common/settings.dart'; + +import 'employees.dart'; + +void main() => runApp(const MaterialApp(home: FleetStep2Page())); + +class FleetStep2Page extends StatefulWidget { + const FleetStep2Page({super.key}); + + @override + State createState() => _FleetStep2PageState(); +} + +class _FleetStep2PageState extends State { + final _formKey = GlobalKey(); + + // Store multiple tanker entries (collected on Add Tanker) + final List> _tankers = []; + + // Minimal payload + clear (no fields on Step 2 yet) + Map _buildPayload() => { + "added_at": DateTime.now().toIso8601String(), + "step": 2, + }; + void _clearForm() { + setState(() {}); + } + + void _addTanker() { + final ok = _formKey.currentState?.validate() ?? true; // no fields => true + if (!ok) return; + _tankers.add(_buildPayload()); + _clearForm(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Tanker added (${_tankers.length})")), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + backgroundColor: Colors.white, + surfaceTintColor: Colors.transparent, + elevation: 0, + scrolledUnderElevation: 0, + title: const Text("Complete Profile"), + actions: [ + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 10), + child: IconButton( + splashRadius: 20, + padding: EdgeInsets.zero, + icon: const Image(image: AssetImage('images/calendar_appbar.png'), width: 22, height: 22), + onPressed: () {}, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0, 10, 10, 10), + child: IconButton( + splashRadius: 20, + padding: EdgeInsets.zero, + icon: Image.asset('images/notification_appbar.png', width: 22, height: 22), + onPressed: () {}, + ), + ), + ], + ), + body: SafeArea( + child: Form( + key: _formKey, + child: ListView( + padding: const EdgeInsets.fromLTRB(20, 10, 20, 24), + children: [ + // Step indicator + Text( + "Step 2/5", + style: fontTextStyle(16, const Color(0xFFC3C4C4), FontWeight.w500), + ), + const SizedBox(height: 16), + + // FLEET header + small image below (left-aligned) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text("FLEET", style: fontTextStyle(20, const Color(0xFF515253), FontWeight.w600)), + const SizedBox(height: 8), + Container( + width: 24, + height: 24, + decoration: const BoxDecoration( + image: DecorationImage(image: AssetImage('images/truck.png'), fit: BoxFit.contain), + ), + ), + ], + ), + + const SizedBox(height: 6), + Text( + "Details about your water tanker fleet", + style: fontTextStyle(14, const Color(0xFF939495), FontWeight.w500), + ), + const SizedBox(height: 16), + + // Header bar ONLY (rounded, separate "card" look) + _SectionHeaderBar( + title: "WATER TANKER #1", + icon: Image.asset('images/arrow-up.png', width: 16, height: 16), + radius: 20, // rounded corners + ), + const SizedBox(height: 12), + + // (No form fields on Step 2 per your snippet) + + const SizedBox(height: 20), + + // Actions + Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SizedBox( + width: double.infinity, + child: OutlinedButton.icon( + onPressed: _addTanker, + icon: Image.asset('images/Add_icon.png', width: 16, height: 16), + label: Text( + "Add Tanker", + style: fontTextStyle(14, const Color(0xFF646566), FontWeight.w600), + ), + ), + ), + const SizedBox(height: 12), + SizedBox( + width: double.infinity, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF8270DB), + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 14), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)), + ), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute(builder: (_) => const FleetEmployees()), + ); + }, + child: Text( + "Continue", + style: fontTextStyle(14, Colors.white, FontWeight.w400), + ), + ), + ), + ], + ), + ], + ), + ), + ), + ); + } +} + +class _SectionHeaderBar extends StatelessWidget { + final String title; + final Widget? icon; + final Color backgroundColor; + final Color borderColor; + final double radius; + + const _SectionHeaderBar({ + required this.title, + this.icon, + this.backgroundColor = const Color(0xFFEEEEEE), + this.borderColor = const Color(0xFFE5E7EB), + this.radius = 12, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: backgroundColor, + border: Border.all(color: borderColor, width: 1), + borderRadius: BorderRadius.circular(radius), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.04), + blurRadius: 6, + offset: const Offset(0, 2), + ), + ], + ), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + child: Row( + children: [ + Expanded( + child: Text( + title, + style: fontTextStyle(12, const Color(0xFF2D2E30), FontWeight.w600), + ), + ), + if (icon != null) icon!, + ], + ), + ); + } +} diff --git a/lib/resources/resources.dart b/lib/resources/resources.dart new file mode 100644 index 0000000..23be4ed --- /dev/null +++ b/lib/resources/resources.dart @@ -0,0 +1,408 @@ +import 'package:flutter/material.dart'; +import 'package:supplier_new/common/settings.dart'; +void main() => runApp(const MaterialApp(home: ResourcesScreen())); + + +class ResourcesScreen extends StatefulWidget { + const ResourcesScreen({super.key}); + @override + State createState() => _ResourcesScreenState(); +} + +class _ResourcesScreenState extends State { + int selectedTab = 0; + String search = ''; + + final List> items = [ + { + 'title': 'Tanker Name', + 'subtitle': 'Drinking water - 10,000 L', + 'status': ['filled', 'available'], + 'code': 'TS 07 J 3492', + 'owner': 'Ramesh Krishna' + }, + { + 'title': 'Drinking Water - 15,000L', + 'subtitle': 'Drinking water - 15,000 L', + 'status': ['empty', 'available'], + 'code': 'TS 07 J 3492', + 'owner': 'Ramesh Krishna' + }, + { + 'title': 'Tanker Name', + 'subtitle': 'Drinking water - 10,000 L', + 'status': ['filled', 'in-use'], + 'code': 'TS 07 J 3492', + 'owner': 'Ramesh Krishna' + }, + { + 'title': 'Drinking Water - 15,000L', + 'subtitle': 'Drinking water - 15,000 L', + 'status': ['empty', 'in-use'], + 'code': 'TS 07 J 3492', + 'owner': 'Ramesh Krishna' + }, + { + 'title': 'Tanker Name', + 'subtitle': 'Drinking water - 10,000 L', + 'status': ['filled', 'maintenance'], + 'code': 'TS 07 J 3492', + 'owner': 'Ramesh Krishna' + }, + ]; + + @override + Widget build(BuildContext context) { + final filtered = items.where((it) { + final q = search.trim().toLowerCase(); + if (q.isEmpty) return true; + return it['title'].toLowerCase().contains(q) || + it['subtitle'].toLowerCase().contains(q) || + it['owner'].toLowerCase().contains(q); + }).toList(); + + return Scaffold( + backgroundColor: const Color(0xFFF6F6F7), + appBar: AppBar( + backgroundColor: Colors.white, + elevation: 0.7, + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.black87), + onPressed: () => Navigator.of(context).maybePop(), + ), + title: Text( + 'Resources', + style: fontTextStyle(16, const Color(0xFF2A2A2A), FontWeight.w600), + ), + centerTitle: false, + actions: [ + TextButton( + onPressed: () {}, + child: Text('HELP', style: fontTextStyle(12, const Color(0xFF8270DB), FontWeight.w600)), + ), + ], + ), + + body: SafeArea( + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 12, 16, 0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Segmented tabs + Container( + padding: const EdgeInsets.all(6), + decoration: BoxDecoration( + color: const Color(0xFFF1F1F3), + borderRadius: BorderRadius.circular(24), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: List.generate(3, (i) { + final labels = ['Fleet', 'Drivers', 'Sources']; + final isSelected = selectedTab == i; + return GestureDetector( + onTap: () => setState(() => selectedTab = i), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), + margin: const EdgeInsets.symmetric(horizontal: 4), + decoration: BoxDecoration( + color: isSelected ? Colors.white : Colors.transparent, + borderRadius: BorderRadius.circular(20), + ), + child: Text( + labels[i], + style: TextStyle( + color: isSelected ? Colors.black87 : Colors.grey.shade700, + fontWeight: isSelected ? FontWeight.w600 : FontWeight.w500, + ), + ), + ), + ); + }), + ), + ), + + const SizedBox(height: 16), + + // Summary card and small stats row + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Big card + Expanded( + flex: 2, + child: Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow(color: Colors.black.withOpacity(0.03), blurRadius: 6, offset: const Offset(0, 3)) + ], + ), + child: Row( + children: [ + Container( + width: 48, + height: 48, + decoration: BoxDecoration( + color: const Color(0xFFF6F0FF), + borderRadius: BorderRadius.circular(10), + ), + child: const Icon(Icons.local_shipping_outlined, color: Color(0xFF6F5FBA)), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: const [ + Text('Total Tankers', style: TextStyle(fontSize: 14, color: Colors.black87)), + SizedBox(height: 6), + Text('14', style: TextStyle(fontSize: 28, fontWeight: FontWeight.w700, color: Color(0xFF173F5F))), + SizedBox(height: 2), + Text('+2 since last month', style: TextStyle(fontSize: 12, color: Colors.grey)), + ], + ), + ), + ], + ), + ), + ), + + const SizedBox(width: 12), + + // Small stat cards + Expanded( + flex: 1, + child: Column( + children: [ + StatCard(title: 'Active', value: '2'), + const SizedBox(height: 8), + StatCard(title: 'Inactive', value: '3'), + const SizedBox(height: 8), + StatCard(title: 'Under\nMaintenances', value: '1'), + ], + ), + ) + ], + ), + + const SizedBox(height: 16), + + // Search and filter row + Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10), + ), + child: Row( + children: [ + const Icon(Icons.search, color: Colors.grey), + const SizedBox(width: 8), + Expanded( + child: TextField( + decoration: const InputDecoration( + hintText: 'Search', + border: InputBorder.none, + isDense: true, + ), + onChanged: (v) => setState(() => search = v), + ), + ), + IconButton( + onPressed: () {}, + icon: const Icon(Icons.filter_list, color: Colors.grey), + ), + IconButton( + onPressed: () {}, + icon: const Icon(Icons.swap_vert, color: Colors.grey), + ) + ], + ), + ), + + const SizedBox(height: 12), + + // List + Expanded( + child: ListView.separated( + itemCount: filtered.length, + separatorBuilder: (_, __) => const SizedBox(height: 10), + itemBuilder: (context, idx) { + final it = filtered[idx]; + return TankCard( + title: it['title'], + subtitle: it['subtitle'], + code: it['code'], + owner: it['owner'], + status: List.from(it['status']), + ); + }, + ), + ), + ], + ), + ), + ), + + floatingActionButton: FloatingActionButton( + onPressed: () {}, + backgroundColor: const Color(0xFF2E2B5F), + child: const Icon(Icons.add), + ), + + bottomNavigationBar: BottomNavigationBar( + type: BottomNavigationBarType.fixed, + currentIndex: 0, + selectedItemColor: const Color(0xFF6F5FBA), + unselectedItemColor: Colors.grey, + items: const [ + BottomNavigationBarItem(icon: Icon(Icons.home_outlined), label: 'Home'), + BottomNavigationBarItem(icon: Icon(Icons.receipt_long_outlined), label: 'Orders'), + BottomNavigationBarItem(icon: Icon(Icons.calendar_month), label: 'Plans'), + BottomNavigationBarItem(icon: Icon(Icons.group_outlined), label: 'Resources'), + BottomNavigationBarItem(icon: Icon(Icons.menu), label: 'More'), + ], + ), + ); + } +} + +class StatCard extends StatelessWidget { + final String title; + final String value; + const StatCard({super.key, required this.title, required this.value}); + @override + Widget build(BuildContext context) { + return Container( + height: 58, + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10), + border: Border.all(color: Colors.grey.shade200), + ), + child: Row( + children: [ + Expanded(child: Text(title, style: const TextStyle(fontSize: 13, color: Colors.black87))), + Text(value, style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w700, color: Color(0xFF173F5F))), + ], + ), + ); + } +} + +class TankCard extends StatelessWidget { + final String title; + final String subtitle; + final String code; + final String owner; + final List status; + const TankCard({ + super.key, + required this.title, + required this.subtitle, + required this.code, + required this.owner, + required this.status, + }); + + Color _chipColor(String s) { + switch (s) { + case 'filled': + return const Color(0xFFE8F7F1); + case 'available': + return const Color(0xFFE8F0FF); + case 'empty': + return const Color(0xFFFFEEEE); + case 'in-use': + return const Color(0xFFFFF0E6); + case 'maintenance': + return const Color(0xFFFFF4E6); + default: + return const Color(0xFFECECEC); + } + } + + Color _chipTextColor(String s) { + switch (s) { + case 'filled': + return Colors.green.shade700; + case 'available': + return const Color(0xFF2E2B5F); + case 'empty': + return Colors.red.shade600; + case 'in-use': + return Colors.orange.shade700; + case 'maintenance': + return Colors.orange.shade700; + default: + return Colors.black87; + } + } + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.grey.shade100), + ), + child: Row( + children: [ + // left column: chips + texts + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // chips + Row( + children: status.map((s) { + return Container( + margin: const EdgeInsets.only(right: 6), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: _chipColor(s), + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.grey.withOpacity(0.12)), + ), + child: Text( + s, + style: TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: _chipTextColor(s)), + ), + ); + }).toList(), + ), + const SizedBox(height: 8), + Text(title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w700)), + const SizedBox(height: 6), + Text(subtitle, style: const TextStyle(fontSize: 13, color: Colors.grey)), + const SizedBox(height: 10), + Row( + children: [ + CircleAvatar(radius: 10, backgroundColor: const Color(0xFFEEF5FF), child: const Icon(Icons.person, size: 12, color: Color(0xFF6F5FBA))), + const SizedBox(width: 6), + Expanded(child: Text(owner, style: const TextStyle(fontSize: 12, color: Colors.grey))), + ], + ), + ], + ), + ), + + // right column: code + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text(code, style: const TextStyle(fontSize: 12, color: Colors.grey)), + const SizedBox(height: 28), + ], + ), + ], + ), + ); + } +} diff --git a/lib/resources/source_location.dart b/lib/resources/source_location.dart new file mode 100644 index 0000000..b17b8df --- /dev/null +++ b/lib/resources/source_location.dart @@ -0,0 +1,429 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:supplier_new/common/settings.dart'; +import 'package:supplier_new/resources/source_location1.dart'; + +import 'Fleet_1.dart'; +void main() => runApp(const MaterialApp(home: SourceLocation())); + +class SourceLocation extends StatefulWidget { + const SourceLocation({super.key}); + + @override + State createState() => _SourceLocationState(); +} + +class _SourceLocationState extends State { + final _formKey = GlobalKey(); + + // Controllers + final _nameCtrl = TextEditingController(); + final _mobileCtrl = TextEditingController(); + final _altMobileCtrl = TextEditingController(); + + // Dropdowns + final List waterTypes = [ + "Drinking Water", + "Industrial", + "Construction", + "Non-potable", + ]; + String? selectedWaterType; + + String? selectedLicense; + + final List yearOptions = ["1", "2", "3", "4", "5"]; + String? selectedExperience; + + // Data bucket + final List> _drivers = []; + + // Validators + String? _required(String? v, {String field = "This field"}) { + if (v == null || v.trim().isEmpty) return "$field is required"; + return null; + } + + String? _validatePhone(String? v, {String label = "Phone Number"}) { + if (v == null || v.trim().isEmpty) return "$label is required"; + final digits = v.replaceAll(RegExp(r'\D'), ''); + if (digits.length != 10) return "Enter a 10-digit $label"; + if (!RegExp(r'^[6-9]\d{9}$').hasMatch(digits)) { + return "$label must start with 6/7/8/9"; + } + return null; + } + + @override + void dispose() { + _nameCtrl.dispose(); + _mobileCtrl.dispose(); + _altMobileCtrl.dispose(); + super.dispose(); + } + bool addBusinessAsSource = false; + + Map _buildPayload() => { + "driver_name": _nameCtrl.text.trim(), + "license_number": selectedLicense, + "experience_years": selectedExperience, + "phone": _mobileCtrl.text.trim(), + "alt_phone": _altMobileCtrl.text.trim(), + }; + + void _clearForm() { + _nameCtrl.clear(); + _mobileCtrl.clear(); + _altMobileCtrl.clear(); + selectedLicense = null; + selectedExperience = null; + setState(() {}); + } + + void _addDriver() { + final ok = _formKey.currentState?.validate() ?? false; + setState(() {}); // ensure error texts render + if (!ok || + selectedLicense == null || + selectedExperience == null || + selectedLicense!.isEmpty || + selectedExperience!.isEmpty) { + if (selectedLicense == null || selectedExperience == null) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("Please select License & Experience")), + ); + } + return; + } + _drivers.add(_buildPayload()); + _clearForm(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Driver added (${_drivers.length})")), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + backgroundColor: Colors.white, + surfaceTintColor: Colors.transparent, + elevation: 0, + scrolledUnderElevation: 0, + title: const Text("Complete Profile"), + actions: [ + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 10), + child: IconButton( + splashRadius: 20, + padding: EdgeInsets.zero, + icon: const Image(image: AssetImage('images/calendar_appbar.png'), width: 22, height: 22), + onPressed: () {}, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0, 10, 10, 10), + child: IconButton( + splashRadius: 20, + padding: EdgeInsets.zero, + icon: Image.asset('images/notification_appbar.png', width: 22, height: 22), + onPressed: () {}, + ), + ), + ], + ), + body: SafeArea( + child: Form( + key: _formKey, + child: ListView( + padding: const EdgeInsets.fromLTRB(20, 10, 20, 24), + children: [ + // Step indicator + Text( + "Step 1/5", + style: fontTextStyle(16, const Color(0xFFC3C4C4), FontWeight.w500), + ), + const SizedBox(height: 16), + Row( + children: List.generate(4, (index) { + return Expanded( + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 2), + height: 5, + decoration: BoxDecoration( + color: index < 4 ? const Color(0xFFC3C4C4) : Colors.grey, + borderRadius: BorderRadius.circular(2), + ), + ), + ); + }), + ), + const SizedBox(height: 12), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("SORURCE LOCATION", style: fontTextStyle(20, const Color(0xFF515253), FontWeight.w600)), + const SizedBox(height: 8), + Container( + width: 24, + height: 24, + decoration: const BoxDecoration( + image: DecorationImage(image: AssetImage('images/flag.png'), fit: BoxFit.contain), + ), + ), + ], + ), + const SizedBox(height: 6), + Text( + "Add your source Location", + style: fontTextStyle(14, const Color(0xFF939495), FontWeight.w500), + ), + const SizedBox(height: 6), + Align( + alignment: Alignment.centerLeft, // keep the whole thing on the left + child: Row( + mainAxisSize: MainAxisSize.min, // don't stretch full width + children: [ + Checkbox( + value: addBusinessAsSource, + onChanged: (v) => setState(() => addBusinessAsSource = v ?? false), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + visualDensity: const VisualDensity(horizontal: -4, vertical: -4), + ), + const SizedBox(width: 6), // control the exact gap + Text( + "Add Business Location as a Source Location", + style: fontTextStyle(14, const Color(0xFF939495), FontWeight.w500), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + + const SizedBox(height: 16), + + // Section header (just the bar) + _SectionHeaderBar( + title: "SOURCE LOCATION #1", + icon: Image.asset('images/arrow-up.png', width: 16, height: 16), + radius: 20, + ), + const SizedBox(height: 12), + + // === Fields + _LabeledField( + label: "Location Name *", + child: TextFormField( + controller: _nameCtrl, + validator: (v) => _required(v, field: "Location Name"), + decoration: InputDecoration( + hintText: "Location Name", + hintStyle: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400), + border: OutlineInputBorder(), + isDense: true, + ), + textInputAction: TextInputAction.next, + ), + ), + + + + _LabeledField( + label: "Mobile Number *", + child: TextFormField( + controller: _mobileCtrl, + validator: (v) => _validatePhone(v, label: "Mobile Number"), + keyboardType: TextInputType.phone, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + LengthLimitingTextInputFormatter(10), + ], + decoration: InputDecoration( + hintText: "Mobile Number", + hintStyle: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400), + border: const OutlineInputBorder(), + isDense: true, + ), + textInputAction: TextInputAction.next, + ), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SizedBox( + width: double.infinity, + child: OutlinedButton.icon( + onPressed: _addDriver, + icon: Image.asset('images/Add_icon.png', width: 16, height: 16), + label: Text( + "Add Location on map", + style: fontTextStyle(14, const Color(0xFF646566), FontWeight.w600), + ), + ), + ), + const SizedBox(height: 12), + + ], + ), + _LabeledField( + label: "Water Type *", + child: DropdownButtonFormField( + value: selectedWaterType, + items: waterTypes + .map((w) => DropdownMenuItem(value: w, child: Text(w))) + .toList(), + onChanged: (v) => setState(() => selectedWaterType = v), + validator: (v) => v == null || v.isEmpty ? "Water Type is required" : null, + isExpanded: true, + alignment: Alignment.centerLeft, + hint: Text( + "Select Water Type", + style: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400), + ), + icon: Image.asset('images/downarrow.png', width: 16, height: 16), + decoration: const InputDecoration( + border: OutlineInputBorder(), + isDense: false, + contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 14), + ), + ), + ), + + + const SizedBox(height: 20), + + // Actions + Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SizedBox( + width: double.infinity, + child: OutlinedButton.icon( + onPressed: _addDriver, + icon: Image.asset('images/Add_icon.png', width: 16, height: 16), + label: Text( + "Add Location", + style: fontTextStyle(14, const Color(0xFF646566), FontWeight.w600), + ), + ), + ), + const SizedBox(height: 12), + SizedBox( + width: double.infinity, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF8270DB), + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 14), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)), + ), + onPressed: () { + // TODO: Navigate to the next step/screen + Navigator.push(context, MaterialPageRoute(builder: (_) => const SourceLocation1())); + // ScaffoldMessenger.of(context).showSnackBar( + // SnackBar(content: Text("Saved ${_drivers.length} driver(s). Proceeding…")), + // ); + }, + child: Text( + "Continue", + style: fontTextStyle(14, Colors.white, FontWeight.w400), + ), + ), + ), + ], + ), + ], + ), + ), + ), + ); + } +} + +// ======= UI helpers ======= + +class _SectionHeaderBar extends StatelessWidget { + final String title; + final Widget? icon; + final Color backgroundColor; + final Color borderColor; + final double radius; + + const _SectionHeaderBar({ + required this.title, + this.icon, + this.backgroundColor = const Color(0xFFEEEEEE), + this.borderColor = const Color(0xFFE5E7EB), + this.radius = 12, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: backgroundColor, + border: Border.all(color: borderColor, width: 1), + borderRadius: BorderRadius.circular(radius), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.04), + blurRadius: 6, + offset: const Offset(0, 2), + ), + ], + ), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + child: Row( + children: [ + Expanded( + child: Text( + title, + style: fontTextStyle(12, const Color(0xFF2D2E30), FontWeight.w600), + ), + ), + if (icon != null) icon!, + ], + ), + ); + } +} + +class _LabeledField extends StatelessWidget { + final String label; + final Widget child; + final String? Function()? validator; // (kept from your earlier helper; not used here) + + const _LabeledField({ + required this.label, + required this.child, + this.validator, + }); + + @override + Widget build(BuildContext context) { + final errorText = validator != null ? validator!() : null; + + return Padding( + padding: const EdgeInsets.only(bottom: 14.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(label, style: fontTextStyle(12, const Color(0xFF515253), FontWeight.w600)), + const SizedBox(height: 6), + child, + if (errorText != null) + Padding( + padding: const EdgeInsets.only(top: 6), + child: Text( + errorText, + style: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400), + ), + ), + ], + ), + ); + } +} diff --git a/lib/resources/source_location1.dart b/lib/resources/source_location1.dart new file mode 100644 index 0000000..060cbb3 --- /dev/null +++ b/lib/resources/source_location1.dart @@ -0,0 +1,315 @@ +import 'package:flutter/material.dart'; +import 'package:supplier_new/common/settings.dart'; +import 'package:supplier_new/resources/source_location2.dart'; + +void main() => runApp(const MaterialApp(home: SourceLocation1())); + +class SourceLocation1 extends StatefulWidget { + const SourceLocation1({super.key}); + + @override + State createState() => _SourceLocation1State(); +} + +class _SourceLocation1State extends State { + final _formKey = GlobalKey(); + + String _deliveryFrom = 'business'; + final TextEditingController _radiusController = + TextEditingController(text: "10"); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + backgroundColor: Colors.white, + surfaceTintColor: Colors.transparent, + elevation: 0, + scrolledUnderElevation: 0, + title: const Text("Complete Profile"), + actions: [ + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 10), + child: IconButton( + splashRadius: 20, + padding: EdgeInsets.zero, + icon: const Image( + image: AssetImage('images/calendar_appbar.png'), + width: 22, + height: 22), + onPressed: () {}, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0, 10, 10, 10), + child: IconButton( + splashRadius: 20, + padding: EdgeInsets.zero, + icon: Image.asset('images/notification_appbar.png', + width: 22, height: 22), + onPressed: () {}, + ), + ), + ], + ), + body: SafeArea( + child: Form( + key: _formKey, + child: ListView( + padding: const EdgeInsets.fromLTRB(20, 10, 20, 24), + children: [ + // Step indicator + Text( + "Step 1/5", + style: + fontTextStyle(16, const Color(0xFFC3C4C4), FontWeight.w500), + ), + const SizedBox(height: 16), + Row( + children: List.generate(4, (index) { + return Expanded( + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 2), + height: 5, + decoration: BoxDecoration( + color: index < 4 ? const Color(0xFFC3C4C4) : Colors.grey, + borderRadius: BorderRadius.circular(2), + ), + ), + ); + }), + ), + const SizedBox(height: 16), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("SORURCE LOCATION", + style: fontTextStyle( + 20, const Color(0xFF515253), FontWeight.w600)), + const SizedBox(height: 8), + Container( + width: 24, + height: 24, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('images/marker-pin.png'), + fit: BoxFit.contain), + ), + ), + ], + ), + const SizedBox(height: 12), + Text( + "Define where you want to provide delivery services", + style: + fontTextStyle(14, const Color(0xFF939495), FontWeight.w500), + maxLines: 1, + softWrap: false, + ), + const SizedBox(height: 6), + // Radio ListTiles (vertical) + Column( + mainAxisSize: MainAxisSize.min, + children: [ + RadioListTile( + value: 'business', + groupValue: _deliveryFrom, + onChanged: (value) { + if (value == null) return; + setState(() => _deliveryFrom = value); + }, + title: Text( + "From Business Location", + style: fontTextStyle( + 14, const Color(0xFF2D2E30), FontWeight.w500), + ), + dense: true, + contentPadding: EdgeInsets.zero, + visualDensity: + const VisualDensity(horizontal: 0, vertical: -3), + ), + RadioListTile( + value: 'source', + groupValue: _deliveryFrom, + onChanged: (value) { + if (value == null) return; + setState(() => _deliveryFrom = value); + }, + title: Text( + "From Source Locations", + style: fontTextStyle( + 14, const Color(0xFF2D2E30), FontWeight.w500), + ), + dense: true, + contentPadding: EdgeInsets.zero, + visualDensity: + const VisualDensity(horizontal: 0, vertical: -3), + ), + ], + ), + + const SizedBox(height: 12), + Text( + "Delivery Radius (in Kms) *", + style: + fontTextStyle(14, const Color(0xFF2D2E30), FontWeight.w500), + ), + const SizedBox(height: 6), + TextFormField( + controller: _radiusController, + keyboardType: TextInputType.number, + decoration: InputDecoration( + hintText: "Enter radius in kms", + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + contentPadding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 12), + ), + ), + const SizedBox(height: 12), + InkWell( + onTap: () => setState( + () => _customizeEachSource = !_customizeEachSource), + child: Padding( + padding: EdgeInsets.zero, + + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + + children: [ + + Checkbox( + value: _customizeEachSource, + onChanged: (val) => + setState(() => _customizeEachSource = val ?? false), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + + const SizedBox(width: 2), + Expanded( + child: Text( + "Customize for every each source location", + style: fontTextStyle( + 14, const Color(0xFF2D2E30), FontWeight.w500), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + + ], + ), + ), + ), + const SizedBox(height: 12), + Container( + width: 343, + height: 166, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('images/google_maps.png'), + fit: BoxFit.contain), + ), + ), + const SizedBox(height: 20), + Text( + "Types of Services", + style: + fontTextStyle(16, const Color(0xFF2D2E30), FontWeight.w600), + ), + const SizedBox(height: 12), + Text( + "Define what type of services you would wish to provide", + style: + fontTextStyle(14, const Color(0xFF939495), FontWeight.w500), + ), + const SizedBox(height: 20), + Row( + children: [ + const Icon(Icons.check_box_outline_blank, size: 20, color: Color(0xFF939495)), + const SizedBox(width: 8), + Text( + "24/7 Emergency services", + style: fontTextStyle(12, const Color(0xFF2D2E30), FontWeight.w400), + ), + ], + ), + const SizedBox(height: 12), + Row( + children: [ + const Icon(Icons.check_box_outline_blank, size: 20, color: Color(0xFF939495)), + const SizedBox(width: 8), + Text( + "Scheduled water deliveries", + style: fontTextStyle(12, const Color(0xFF2D2E30), FontWeight.w400), + ), + ], + ), + const SizedBox(height: 12), + Row( + children: [ + const Icon(Icons.check_box_outline_blank, size: 20, color: Color(0xFF939495)), + const SizedBox(width: 8), + Text( + "Bulk water deliveries", + style: fontTextStyle(12, const Color(0xFF2D2E30), FontWeight.w400), + ), + ], + ), + const SizedBox(height: 12), + Row( + children: [ + const Icon(Icons.check_box_outline_blank, size: 20, color: Color(0xFF939495)), + const SizedBox(width: 8), + Text( + "Long-term water delivery planes", + style: fontTextStyle(12, const Color(0xFF2D2E30), FontWeight.w400), + ), + ], + ), + const SizedBox(height: 12), + Row( + children: [ + const Icon(Icons.check_box_outline_blank, size: 20, color: Color(0xFF939495)), + const SizedBox(width: 8), + Text( + "Industrial/Commercial water deliveries", + style: fontTextStyle(12, const Color(0xFF2D2E30), FontWeight.w400), + ), + ], + ), + const SizedBox(height: 16), + SizedBox( + width: double.infinity, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF8270DB), + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 14), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)), + ), + onPressed: () { + // TODO: Navigate to the next step/screen + Navigator.push(context, MaterialPageRoute(builder: (_) => const SourceLocation2())); + // ScaffoldMessenger.of(context).showSnackBar( + // SnackBar(content: Text("Saved ${_drivers.length} driver(s). Proceeding…")), + // ); + }, + child: Text( + "Continue", + style: fontTextStyle(14, Colors.white, FontWeight.w400), + ), + ), + ), + ], + ), + ), + ), + ); + } + + bool _customizeEachSource = false; +} + + diff --git a/lib/resources/source_location2.dart b/lib/resources/source_location2.dart new file mode 100644 index 0000000..56e3aea --- /dev/null +++ b/lib/resources/source_location2.dart @@ -0,0 +1,416 @@ +import 'package:flutter/material.dart'; +import 'package:supplier_new/common/settings.dart'; +import 'package:supplier_new/resources/source_location2.dart'; + +import 'availability.dart'; + +void main() => runApp(const MaterialApp(home: SourceLocation2())); + +class SourceLocation2 extends StatefulWidget { + const SourceLocation2({super.key}); + + @override + State createState() => _SourceLocation2State(); +} + +class _SourceLocation2State extends State { + final _formKey = GlobalKey(); + + String _deliveryFrom = 'business'; + final TextEditingController _radiusController = + TextEditingController(text: "10"); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + backgroundColor: Colors.white, + surfaceTintColor: Colors.transparent, + elevation: 0, + scrolledUnderElevation: 0, + title: const Text("Complete Profile"), + actions: [ + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 10), + child: IconButton( + splashRadius: 20, + padding: EdgeInsets.zero, + icon: const Image( + image: AssetImage('images/calendar_appbar.png'), + width: 22, + height: 22), + onPressed: () {}, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0, 10, 10, 10), + child: IconButton( + splashRadius: 20, + padding: EdgeInsets.zero, + icon: Image.asset('images/notification_appbar.png', + width: 22, height: 22), + onPressed: () {}, + ), + ), + ], + ), + body: SafeArea( + child: Form( + key: _formKey, + child: ListView( + padding: const EdgeInsets.fromLTRB(20, 10, 20, 24), + children: [ + // Step indicator + Text( + "Step 1/5", + style: + fontTextStyle(16, const Color(0xFFC3C4C4), FontWeight.w500), + ), + const SizedBox(height: 16), + Row( + children: List.generate(4, (index) { + return Expanded( + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 2), + height: 5, + decoration: BoxDecoration( + color: index < 4 ? const Color(0xFFC3C4C4) : Colors.grey, + borderRadius: BorderRadius.circular(2), + ), + ), + ); + }), + ), + const SizedBox(height: 16), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("SORURCE LOCATION", + style: fontTextStyle( + 20, const Color(0xFF515253), FontWeight.w600)), + const SizedBox(height: 8), + Container( + width: 24, + height: 24, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('images/marker-pin.png'), + fit: BoxFit.contain), + ), + ), + ], + ), + const SizedBox(height: 12), + Text( + "Define where you want to provide delivery services", + style: + fontTextStyle(14, const Color(0xFF939495), FontWeight.w500), + maxLines: 1, + softWrap: false, + ), + const SizedBox(height: 6), + // Radio ListTiles (vertical) + Column( + mainAxisSize: MainAxisSize.min, + children: [ + RadioListTile( + value: 'business', + groupValue: _deliveryFrom, + onChanged: (value) { + if (value == null) return; + setState(() => _deliveryFrom = value); + }, + title: Text( + "From Business Location", + style: fontTextStyle( + 14, const Color(0xFF2D2E30), FontWeight.w500), + ), + dense: true, + contentPadding: EdgeInsets.zero, + visualDensity: + const VisualDensity(horizontal: 0, vertical: -3), + ), + RadioListTile( + value: 'source', + groupValue: _deliveryFrom, + onChanged: (value) { + if (value == null) return; + setState(() => _deliveryFrom = value); + }, + title: Text( + "From Source Locations", + style: fontTextStyle( + 14, const Color(0xFF2D2E30), FontWeight.w500), + ), + dense: true, + contentPadding: EdgeInsets.zero, + visualDensity: + const VisualDensity(horizontal: 0, vertical: -3), + ), + ], + ), + + const SizedBox(height: 12), + Text( + "Delivery Radius (in Kms) *", + style: + fontTextStyle(14, const Color(0xFF2D2E30), FontWeight.w500), + ), + const SizedBox(height: 6), + TextFormField( + controller: _radiusController, + keyboardType: TextInputType.number, + decoration: InputDecoration( + hintText: "Enter radius in kms", + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + contentPadding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 12), + ), + ), + const SizedBox(height: 12), + InkWell( + onTap: () => setState( + () => _customizeEachSource = !_customizeEachSource), + child: Padding( + padding: EdgeInsets.zero, + + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + + children: [ + + Checkbox( + value: _customizeEachSource, + onChanged: (val) => + setState(() => _customizeEachSource = val ?? false), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + + const SizedBox(width: 2), + Expanded( + child: Text( + "Customize for every each source location", + style: fontTextStyle( + 14, const Color(0xFF2D2E30), FontWeight.w500), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + + ], + ), + ), + ), + const SizedBox(height: 12), + _SectionHeaderBar( + title: "SOURCE LOCATION #1", + radius: 29, + ), + + const SizedBox(height: 12), + Text( + "Delivery Radius (in Kms) *", + style: + fontTextStyle(14, const Color(0xFF2D2E30), FontWeight.w500), + ), + const SizedBox(height: 6), + TextFormField( + controller: _radiusController, + keyboardType: TextInputType.number, + decoration: InputDecoration( + hintText: "Enter radius in kms", + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + contentPadding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 12), + ), + ), + const SizedBox(height: 12), + _SectionHeaderBar( + title: "SOURCE LOCATION #2", + radius: 29, + ), + + const SizedBox(height: 12), + Text( + "Delivery Radius (in Kms) *", + style: + fontTextStyle(14, const Color(0xFF2D2E30), FontWeight.w500), + ), + const SizedBox(height: 6), + TextFormField( + controller: _radiusController, + keyboardType: TextInputType.number, + decoration: InputDecoration( + hintText: "Enter radius in kms", + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + contentPadding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 12), + ), + ), + + const SizedBox(height: 12), + Container( + width: 343, + height: 166, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('images/google_maps.png'), + fit: BoxFit.contain), + ), + ), + const SizedBox(height: 20), + Text( + "Types of Services", + style: + fontTextStyle(16, const Color(0xFF2D2E30), FontWeight.w600), + ), + const SizedBox(height: 12), + Text( + "Define what type of services you would wish to provide", + style: + fontTextStyle(14, const Color(0xFF939495), FontWeight.w500), + ), + const SizedBox(height: 20), + Row( + children: [ + const Icon(Icons.check_box_outline_blank, size: 20, color: Color(0xFF939495)), + const SizedBox(width: 8), + Text( + "24/7 Emergency services", + style: fontTextStyle(12, const Color(0xFF2D2E30), FontWeight.w400), + ), + ], + ), + const SizedBox(height: 12), + Row( + children: [ + const Icon(Icons.check_box_outline_blank, size: 20, color: Color(0xFF939495)), + const SizedBox(width: 8), + Text( + "Scheduled water deliveries", + style: fontTextStyle(12, const Color(0xFF2D2E30), FontWeight.w400), + ), + ], + ), + const SizedBox(height: 12), + Row( + children: [ + const Icon(Icons.check_box_outline_blank, size: 20, color: Color(0xFF939495)), + const SizedBox(width: 8), + Text( + "Bulk water deliveries", + style: fontTextStyle(12, const Color(0xFF2D2E30), FontWeight.w400), + ), + ], + ), + const SizedBox(height: 12), + Row( + children: [ + const Icon(Icons.check_box_outline_blank, size: 20, color: Color(0xFF939495)), + const SizedBox(width: 8), + Text( + "Long-term water delivery planes", + style: fontTextStyle(12, const Color(0xFF2D2E30), FontWeight.w400), + ), + ], + ), + const SizedBox(height: 12), + Row( + children: [ + const Icon(Icons.check_box_outline_blank, size: 20, color: Color(0xFF939495)), + const SizedBox(width: 8), + Text( + "Industrial/Commercial water deliveries", + style: fontTextStyle(12, const Color(0xFF2D2E30), FontWeight.w400), + ), + ], + ), + const SizedBox(height: 12), + SizedBox( + width: double.infinity, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF8270DB), + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 14), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)), + ), + onPressed: () { + // TODO: Navigate to the next step/screen + Navigator.push(context, MaterialPageRoute(builder: (_) => const AvailabilityScreen())); + // ScaffoldMessenger.of(context).showSnackBar( + // SnackBar(content: Text("Saved ${_drivers.length} driver(s). Proceeding…")), + // ); + }, + child: Text( + " Save \$ Continue", + style: fontTextStyle(14, Colors.white, FontWeight.w400), + ), + + ), + ), + + ], + ), + ), + ), + ); + } + + bool _customizeEachSource = false; +} + +class _SectionHeaderBar extends StatelessWidget { + final String title; + final Widget? icon; + final Color backgroundColor; + final Color borderColor; + final double radius; + + const _SectionHeaderBar({ + required this.title, + this.icon, + this.backgroundColor = const Color(0xFFEEEEEE), + this.borderColor = const Color(0xFFE5E7EB), + this.radius = 12, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: backgroundColor, + border: Border.all(color: borderColor, width: 1), + borderRadius: BorderRadius.circular(radius), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.04), + blurRadius: 6, + offset: const Offset(0, 2), + ), + ], + ), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), + child: Row( + children: [ + Expanded( + child: Text( + title, + style: fontTextStyle(10, const Color(0xFF2D2E30), FontWeight.w600), + ), + ), + if (icon != null) icon!, + ], + ), + ); + } +} + diff --git a/lib/signup/password_textbox_screen.dart b/lib/signup/password_textbox_screen.dart index 20c1a43..10e0540 100644 --- a/lib/signup/password_textbox_screen.dart +++ b/lib/signup/password_textbox_screen.dart @@ -126,7 +126,7 @@ class _PasswordTextBoxesScreenState extends State { suffixIcon: IconButton( icon: isObscureTextForReEnter==true?Image.asset('images/eye_icon.png',color: Color(0XFF7E7F80),width: 16,height: 16,):Image.asset('images/open_eye.png',color:Color(0XFF7E7F80),width: 16,height: 16,), /* Icon( - icon:Image.asset('assets/your_image.png'), + icon:Image.asset('assets/your_image.png'),x color: isObscureText==true?greyColor:primaryColor, ),*/ onPressed: () {