import 'package:flutter/material.dart'; /// Vordefinierte Gründe für Abbruch / Teilabbruch. /// /// Die Liste ist absichtlich kurz und fahrernah gehalten — wir fragen keine /// Mini-Romane ab, sondern erlauben das Wichtigste mit einem Tap. Für alles /// Sonstige steht "Anderer Grund" mit Freitext zur Verfügung. const List _predefinedReasons = [ "Kunde nicht erreichbar", "Adresse falsch", "Ware beschädigt", "Zugang nicht möglich", "Anderer Grund", ]; /// Schlüssel-Konstante für die "Anderer Grund"-Option — damit Aufrufer den /// Vergleich nicht über String-Literals führen müssen. const String _otherReasonOption = "Anderer Grund"; /// Wiederverwendbarer Grund-Dialog für Beladen-Phase: sowohl der komplette /// Lieferungs-Abbruch als auch das Zurückhalten einzelner Artikel / /// Komponenten landen in diesem Picker. /// /// Liefert per `showDialog` den finalen Grundtext zurück — also /// entweder einen der vordefinierten Strings oder den vom Fahrer /// eingegebenen Freitext. Bei Abbruch des Dialogs ist das Ergebnis `null`. class ReasonPickerDialog extends StatefulWidget { const ReasonPickerDialog({ super.key, required this.title, this.subtitle, }); /// Anzeigetitel des Dialogs (z. B. "Lieferung abbrechen"). final String title; /// Optionaler erläuternder Untertitel (z. B. Name des Kunden). final String? subtitle; /// Komfort-Helfer: zeigt den Dialog und liefert das Ergebnis. Aufrufer /// müssen so nicht mehr selbst `showDialog` mit dem Builder /// instanziieren. static Future show( BuildContext context, { required String title, String? subtitle, }) { return showDialog( context: context, builder: (_) => ReasonPickerDialog(title: title, subtitle: subtitle), ); } @override State createState() => _ReasonPickerDialogState(); } class _ReasonPickerDialogState extends State { String? _selected; final TextEditingController _freeText = TextEditingController(); @override void dispose() { _freeText.dispose(); super.dispose(); } bool get _isOther => _selected == _otherReasonOption; bool get _canConfirm { if (_selected == null) return false; if (_isOther) return _freeText.text.trim().isNotEmpty; return true; } void _confirm() { if (!_canConfirm) return; final reason = _isOther ? _freeText.text.trim() : _selected!; Navigator.of(context).pop(reason); } @override Widget build(BuildContext context) { return AlertDialog( title: Text(widget.title), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ if (widget.subtitle != null) ...[ Text( widget.subtitle!, style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 12), ], ..._predefinedReasons.map((reason) { return RadioListTile( contentPadding: EdgeInsets.zero, dense: true, title: Text(reason), value: reason, groupValue: _selected, onChanged: (val) => setState(() => _selected = val), ); }), if (_isOther) Padding( padding: const EdgeInsets.only(top: 4), child: TextField( controller: _freeText, autofocus: true, maxLines: 3, minLines: 2, decoration: const InputDecoration( labelText: "Bitte Grund angeben", border: OutlineInputBorder(), ), onChanged: (_) => setState(() {}), ), ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text("Abbrechen"), ), FilledButton( onPressed: _canConfirm ? _confirm : null, child: const Text("Bestätigen"), ), ], ); } }