Add input validation for collection names (min 2, max 50 chars)

Co-authored-by: derkauzigekoala <79001016+derkauzigekoala@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-02-24 19:20:50 +00:00
parent 0539a7725a
commit f789bb7eff
2 changed files with 63 additions and 37 deletions

View file

@ -50,6 +50,7 @@ class CollectionsScreenState extends State<CollectionsScreen> {
} }
Future<void> _createCollection() async { Future<void> _createCollection() async {
final formKey = GlobalKey<FormState>();
final nameCtrl = TextEditingController(); final nameCtrl = TextEditingController();
final descCtrl = TextEditingController(); final descCtrl = TextEditingController();
@ -65,26 +66,37 @@ class CollectionsScreenState extends State<CollectionsScreen> {
child: const Icon(Icons.add, color: Colors.white, size: 28), child: const Icon(Icons.add, color: Colors.white, size: 28),
), ),
title: const Text('New Collection'), title: const Text('New Collection'),
content: Column( content: Form(
mainAxisSize: MainAxisSize.min, key: formKey,
children: [ child: Column(
TextField( mainAxisSize: MainAxisSize.min,
controller: nameCtrl, children: [
autofocus: true, TextFormField(
decoration: const InputDecoration( controller: nameCtrl,
labelText: 'Name', autofocus: true,
hintText: 'e.g. Hot Wheels, Matchbox…', maxLength: 50,
decoration: const InputDecoration(
labelText: 'Name',
hintText: 'e.g. Hot Wheels, Matchbox…',
),
validator: (value) {
final trimmed = value?.trim() ?? '';
if (trimmed.isEmpty) return 'Name is required';
if (trimmed.length < 2) return 'Name must be at least 2 characters';
if (trimmed.length > 50) return 'Name must be 50 characters or fewer';
return null;
},
), ),
), const SizedBox(height: 12),
const SizedBox(height: 12), TextField(
TextField( controller: descCtrl,
controller: descCtrl, decoration: const InputDecoration(
decoration: const InputDecoration( labelText: 'Description (optional)',
labelText: 'Description (optional)', hintText: 'What is this collection for?',
hintText: 'What is this collection for?', ),
), ),
), ],
], ),
), ),
actions: [ actions: [
TextButton( TextButton(
@ -93,8 +105,9 @@ class CollectionsScreenState extends State<CollectionsScreen> {
), ),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
if (nameCtrl.text.trim().isEmpty) return; if (formKey.currentState!.validate()) {
Navigator.pop(context, true); Navigator.pop(context, true);
}
}, },
child: const Text('Create'), child: const Text('Create'),
), ),

View file

@ -44,6 +44,7 @@ class _ManageCollectionScreenState extends State<ManageCollectionScreen> {
} }
Future<void> _rename() async { Future<void> _rename() async {
final formKey = GlobalKey<FormState>();
final ctrl = TextEditingController(text: _collection.name); final ctrl = TextEditingController(text: _collection.name);
final descCtrl = final descCtrl =
TextEditingController(text: _collection.description ?? ''); TextEditingController(text: _collection.description ?? '');
@ -52,21 +53,32 @@ class _ManageCollectionScreenState extends State<ManageCollectionScreen> {
context: context, context: context,
builder: (_) => AlertDialog( builder: (_) => AlertDialog(
title: const Text('Rename Collection'), title: const Text('Rename Collection'),
content: Column( content: Form(
mainAxisSize: MainAxisSize.min, key: formKey,
children: [ child: Column(
TextField( mainAxisSize: MainAxisSize.min,
controller: ctrl, children: [
autofocus: true, TextFormField(
decoration: const InputDecoration(labelText: 'Name'), controller: ctrl,
), autofocus: true,
const SizedBox(height: 12), maxLength: 50,
TextField( decoration: const InputDecoration(labelText: 'Name'),
controller: descCtrl, validator: (value) {
decoration: final trimmed = value?.trim() ?? '';
const InputDecoration(labelText: 'Description (optional)'), if (trimmed.isEmpty) return 'Name is required';
), if (trimmed.length < 2) return 'Name must be at least 2 characters';
], if (trimmed.length > 50) return 'Name must be 50 characters or fewer';
return null;
},
),
const SizedBox(height: 12),
TextField(
controller: descCtrl,
decoration:
const InputDecoration(labelText: 'Description (optional)'),
),
],
),
), ),
actions: [ actions: [
TextButton( TextButton(
@ -75,8 +87,9 @@ class _ManageCollectionScreenState extends State<ManageCollectionScreen> {
), ),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
if (ctrl.text.trim().isEmpty) return; if (formKey.currentState!.validate()) {
Navigator.pop(context, true); Navigator.pop(context, true);
}
}, },
child: const Text('Save'), child: const Text('Save'),
), ),