From 7635b0d85bb4676391663d54348304d9f5704123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20M=C3=BCllner?= Date: Mon, 23 Feb 2026 09:47:46 +0100 Subject: [PATCH] fix: extract manual entry dialog into StatefulWidget Fixes TextEditingController used after being disposed the controller was being manually disposed while the dialog's TextField still held a reference during the pop animation. Now the dialog is its own StatefulWidget so dispose() is called automatically by the framework at the right time. --- lib/scanner_screen.dart | 81 ++++++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 30 deletions(-) diff --git a/lib/scanner_screen.dart b/lib/scanner_screen.dart index 9ff5d3d..0fa27e8 100644 --- a/lib/scanner_screen.dart +++ b/lib/scanner_screen.dart @@ -212,42 +212,63 @@ class _ScannerScreenState extends State { /// Fallback: let the user type the HW ID manually. Future _showManualEntry(BuildContext context) async { - final controller = TextEditingController(); - final result = await showDialog( context: context, - builder: (ctx) => AlertDialog( - title: const Text('Enter HW ID'), - content: TextField( - controller: controller, - autofocus: true, - textCapitalization: TextCapitalization.characters, - decoration: const InputDecoration( - hintText: 'e.g. JKF21', - border: OutlineInputBorder(), - ), - ), - actions: [ - TextButton( - onPressed: () => Navigator.of(ctx).pop(), - child: const Text('Cancel'), - ), - ElevatedButton( - onPressed: () { - final value = controller.text.trim().toUpperCase(); - if (value.isNotEmpty) Navigator.of(ctx).pop(value); - }, - child: const Text('OK'), - ), - ], - ), + builder: (_) => const _ManualEntryDialog(), ); if (result != null && context.mounted) { - controller.dispose(); Navigator.of(context).pop(result); - } else { - controller.dispose(); } } } + +// ── Manual Entry Dialog ─────────────────────────────────────────────── +class _ManualEntryDialog extends StatefulWidget { + const _ManualEntryDialog(); + + @override + State<_ManualEntryDialog> createState() => _ManualEntryDialogState(); +} + +class _ManualEntryDialogState extends State<_ManualEntryDialog> { + final _controller = TextEditingController(); + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + void _submit() { + final value = _controller.text.trim().toUpperCase(); + if (value.isNotEmpty) Navigator.of(context).pop(value); + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Enter HW ID'), + content: TextField( + controller: _controller, + autofocus: true, + textCapitalization: TextCapitalization.characters, + decoration: const InputDecoration( + hintText: 'e.g. JKF21', + border: OutlineInputBorder(), + ), + onSubmitted: (_) => _submit(), + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Cancel'), + ), + ElevatedButton( + onPressed: _submit, + child: const Text('OK'), + ), + ], + ); + } +}