import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; void main() => runApp(const MaterialApp(home: FleetStep1Page())); class FleetStep1Page extends StatefulWidget { const FleetStep1Page({super.key}); @override State createState() => _FleetStep1PageState(); } class _FleetStep1PageState extends State { final _formKey = GlobalKey(); // Controllers final _nameCtrl = TextEditingController(); final _capacityCtrl = TextEditingController(text: "10,000"); // hint-like final _plateCtrl = TextEditingController(text: "AB 05 H 4948"); final _mfgYearCtrl = TextEditingController(); final _insExpiryCtrl = TextEditingController(); // Dropdowns / selections final List tankerTypes = [ "Rigid Truck", "Trailer", "Mini Tanker", "Hydraulic", ]; String? selectedType; final List featureOptions = [ "GPS", "Stainless Steel", "Partitioned", "Food Grade", "Top Loading", "Bottom Loading", ]; final Set selectedFeatures = {}; // Helpers Future _pickInsuranceDate() async { final now = DateTime.now(); final date = await showDatePicker( context: context, initialDate: now, firstDate: DateTime(now.year - 1), lastDate: DateTime(now.year + 10), 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')}"; setState(() {}); } } String? _required(String? v, {String field = "This field"}) { if (v == null || v.trim().isEmpty) return "$field is required"; return null; } @override void dispose() { _nameCtrl.dispose(); _capacityCtrl.dispose(); _plateCtrl.dispose(); _mfgYearCtrl.dispose(); _insExpiryCtrl.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final theme = Theme.of(context); final grey = Colors.grey.shade600; return Scaffold( appBar: AppBar( 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), ), ], ), body: SafeArea( child: Form( key: _formKey, child: ListView( padding: const EdgeInsets.fromLTRB(20, 10, 20, 24), children: [ Text("Step 1/5", style: theme.textTheme.labelLarge?.copyWith(color: grey)), const SizedBox(height: 16), // FLEET header Row( children: [ Icon(Icons.local_shipping_outlined, color: Colors.deepPurple.shade400), const SizedBox(width: 8), Text("FLEET", style: theme.textTheme.titleMedium?.copyWith(letterSpacing: 1.2)), ], ), const SizedBox(height: 6), Text( "Details about your water tanker fleet", style: theme.textTheme.bodySmall?.copyWith(color: grey), ), const SizedBox(height: 16), // Water Tanker card _SectionCard( 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 ), ), _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: 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")), ); }, child: const Text("Save & Continue"), ), ], ), ), ], ), ), ), ); } } /// Section card with a folding-style title bar (static in this example) class _SectionCard extends StatelessWidget { final String title; final Widget child; const _SectionCard({required this.title, required this.child}); @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), ], ), const Divider(height: 20), child, ], ), ), ); } } class _LabeledField extends StatelessWidget { final String label; final Widget child; final String? Function()? validator; const _LabeledField({ required this.label, required this.child, this.validator, }); @override Widget build(BuildContext context) { 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( padding: const EdgeInsets.only(bottom: 14.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: labelStyle), 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)), ), ], ), ); } } 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); }, ), ], ); } }