Fix quick-capture dialog controller disposal race

This commit is contained in:
Rijad Zuzo
2026-02-22 23:53:22 +01:00
parent 1f31a0e769
commit 05588cc58e
3 changed files with 181 additions and 58 deletions
+72 -29
View File
@@ -672,39 +672,12 @@ class PeopleView extends ConsumerWidget {
required String title,
required String hintText,
}) async {
final TextEditingController controller = TextEditingController();
final String? result = await showDialog<String>(
return showDialog<String>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(title),
content: TextField(
controller: controller,
minLines: 2,
maxLines: 4,
decoration: InputDecoration(hintText: hintText),
),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Cancel'),
),
FilledButton(
onPressed: () {
final String value = controller.text.trim();
if (value.isEmpty) {
return;
}
Navigator.of(context).pop(value);
},
child: const Text('Save'),
),
],
);
return _QuickTextCaptureDialog(title: title, hintText: hintText);
},
);
controller.dispose();
return result;
}
}
@@ -2046,6 +2019,76 @@ class _PersonEditorDialog extends StatelessWidget {
}
}
class _QuickTextCaptureDialog extends StatefulWidget {
const _QuickTextCaptureDialog({required this.title, required this.hintText});
final String title;
final String hintText;
@override
State<_QuickTextCaptureDialog> createState() =>
_QuickTextCaptureDialogState();
}
class _QuickTextCaptureDialogState extends State<_QuickTextCaptureDialog> {
late final TextEditingController _controller;
bool _attemptedSubmit = false;
@override
void initState() {
super.initState();
_controller = TextEditingController();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final String value = _controller.text.trim();
return AlertDialog(
title: Text(widget.title),
content: TextField(
controller: _controller,
minLines: 2,
maxLines: 4,
autofocus: true,
onChanged: (_) {
if (_attemptedSubmit) {
setState(() {});
}
},
decoration: InputDecoration(
hintText: widget.hintText,
errorText: _attemptedSubmit && value.isEmpty ? 'Required' : null,
),
),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Cancel'),
),
FilledButton(
onPressed: () {
final String submitted = _controller.text.trim();
if (submitted.isEmpty) {
setState(() {
_attemptedSubmit = true;
});
return;
}
Navigator.of(context).pop(submitted);
},
child: const Text('Save'),
),
],
);
}
}
class _PersonEditorBottomSheet extends StatelessWidget {
const _PersonEditorBottomSheet({
required this.title,