fix: extract collection dialogs into own StatefulWidgets

Same _dependents.isEmpty crash as the password dialog  the Add/Already
Exists dialogs were sharing the parent context across async DB calls.
Now each dialog is an independent widget with its own lifecycle.
This commit is contained in:
Lukas Müllner 2026-02-23 08:45:27 +01:00
parent 8c453e3f13
commit 7a6975e146

View file

@ -380,15 +380,12 @@ class HomeScreen extends StatelessWidget {
/// Opens the scanner, gets the hw_id, then queries the DB.
Future<void> _openScanner(BuildContext context) async {
// 1. Navigate to the scanner screen and wait for the result.
final hwId = await Navigator.of(context).push<String>(
MaterialPageRoute(builder: (_) => const ScannerScreen()),
);
// User cancelled / went back without selecting an ID.
if (hwId == null || !context.mounted) return;
// 2. Query the database.
try {
final data = await supabase
.from('hotwheels')
@ -399,11 +396,15 @@ class HomeScreen extends StatelessWidget {
if (!context.mounted) return;
if (data != null) {
// Car already exists
_showAlreadyExistsDialog(context, hwId);
showDialog<void>(
context: context,
builder: (_) => _AlreadyExistsDialog(hwId: hwId),
);
} else {
// Car is new offer to add it
_showAddDialog(context, hwId);
showDialog<void>(
context: context,
builder: (_) => _AddCarDialog(hwId: hwId),
);
}
} catch (e) {
if (!context.mounted) return;
@ -412,62 +413,85 @@ class HomeScreen extends StatelessWidget {
);
}
}
}
/// Shows an alert: this car is already in the collection.
void _showAlreadyExistsDialog(BuildContext context, String hwId) {
showDialog<void>(
context: context,
builder: (ctx) => AlertDialog(
// "Already in Collection" Dialog
class _AlreadyExistsDialog extends StatelessWidget {
final String hwId;
const _AlreadyExistsDialog({required this.hwId});
@override
Widget build(BuildContext context) {
return AlertDialog(
icon: const Icon(Icons.check_circle, color: Colors.green, size: 48),
title: const Text('Already in Collection!'),
content: Text('$hwId is already in your shared garage.'),
actions: [
ElevatedButton(
onPressed: () => Navigator.of(ctx).pop(),
onPressed: () => Navigator.of(context).pop(),
child: const Text('Got it'),
),
],
),
);
}
}
// "Add to Collection" Dialog
class _AddCarDialog extends StatefulWidget {
final String hwId;
const _AddCarDialog({required this.hwId});
@override
State<_AddCarDialog> createState() => _AddCarDialogState();
}
class _AddCarDialogState extends State<_AddCarDialog> {
bool _isAdding = false;
Future<void> _addCar() async {
setState(() => _isAdding = true);
/// Shows a confirmation dialog to add a new car.
void _showAddDialog(BuildContext context, String hwId) {
showDialog<void>(
context: context,
builder: (ctx) => AlertDialog(
icon: const Icon(Icons.add_circle_outline, color: Colors.deepPurple, size: 48),
title: const Text('New Car Found!'),
content: Text('$hwId is not in your collection yet.\nAdd it now?'),
actions: [
TextButton(
onPressed: () => Navigator.of(ctx).pop(),
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: () async {
try {
await supabase.from('hotwheels').insert({
'hw_id': hwId,
'hw_id': widget.hwId,
'user_id': supabase.auth.currentUser!.id,
});
if (!ctx.mounted) return;
Navigator.of(ctx).pop();
if (!mounted) return;
Navigator.of(context).pop();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('$hwId added to your collection! 🎉')),
SnackBar(content: Text('${widget.hwId} added to your collection! 🎉')),
);
} catch (e) {
if (!ctx.mounted) return;
Navigator.of(ctx).pop();
if (!mounted) return;
setState(() => _isAdding = false);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to add: $e'), backgroundColor: Colors.red),
);
}
},
child: const Text('Add to Collection'),
}
@override
Widget build(BuildContext context) {
return AlertDialog(
icon: const Icon(Icons.add_circle_outline, color: Colors.deepPurple, size: 48),
title: const Text('New Car Found!'),
content: Text('${widget.hwId} is not in your collection yet.\nAdd it now?'),
actions: [
TextButton(
onPressed: _isAdding ? null : () => Navigator.of(context).pop(),
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: _isAdding ? null : _addCar,
child: _isAdding
? const SizedBox(
height: 18,
width: 18,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Text('Add to Collection'),
),
],
),
);
}
}