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:
parent
8c453e3f13
commit
7a6975e146
1 changed files with 85 additions and 61 deletions
100
lib/main.dart
100
lib/main.dart
|
|
@ -380,15 +380,12 @@ class HomeScreen extends StatelessWidget {
|
||||||
|
|
||||||
/// Opens the scanner, gets the hw_id, then queries the DB.
|
/// Opens the scanner, gets the hw_id, then queries the DB.
|
||||||
Future<void> _openScanner(BuildContext context) async {
|
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>(
|
final hwId = await Navigator.of(context).push<String>(
|
||||||
MaterialPageRoute(builder: (_) => const ScannerScreen()),
|
MaterialPageRoute(builder: (_) => const ScannerScreen()),
|
||||||
);
|
);
|
||||||
|
|
||||||
// User cancelled / went back without selecting an ID.
|
|
||||||
if (hwId == null || !context.mounted) return;
|
if (hwId == null || !context.mounted) return;
|
||||||
|
|
||||||
// 2. Query the database.
|
|
||||||
try {
|
try {
|
||||||
final data = await supabase
|
final data = await supabase
|
||||||
.from('hotwheels')
|
.from('hotwheels')
|
||||||
|
|
@ -399,11 +396,15 @@ class HomeScreen extends StatelessWidget {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
|
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
// ── Car already exists ──
|
showDialog<void>(
|
||||||
_showAlreadyExistsDialog(context, hwId);
|
context: context,
|
||||||
|
builder: (_) => _AlreadyExistsDialog(hwId: hwId),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// ── Car is new — offer to add it ──
|
showDialog<void>(
|
||||||
_showAddDialog(context, hwId);
|
context: context,
|
||||||
|
builder: (_) => _AddCarDialog(hwId: hwId),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
|
|
@ -412,62 +413,85 @@ class HomeScreen extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Shows an alert: this car is already in the collection.
|
// ── "Already in Collection" Dialog ────────────────────────────────────
|
||||||
void _showAlreadyExistsDialog(BuildContext context, String hwId) {
|
class _AlreadyExistsDialog extends StatelessWidget {
|
||||||
showDialog<void>(
|
final String hwId;
|
||||||
context: context,
|
const _AlreadyExistsDialog({required this.hwId});
|
||||||
builder: (ctx) => AlertDialog(
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
icon: const Icon(Icons.check_circle, color: Colors.green, size: 48),
|
icon: const Icon(Icons.check_circle, color: Colors.green, size: 48),
|
||||||
title: const Text('Already in Collection!'),
|
title: const Text('Already in Collection!'),
|
||||||
content: Text('$hwId is already in your shared garage.'),
|
content: Text('$hwId is already in your shared garage.'),
|
||||||
actions: [
|
actions: [
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => Navigator.of(ctx).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
child: const Text('Got it'),
|
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 {
|
try {
|
||||||
await supabase.from('hotwheels').insert({
|
await supabase.from('hotwheels').insert({
|
||||||
'hw_id': hwId,
|
'hw_id': widget.hwId,
|
||||||
'user_id': supabase.auth.currentUser!.id,
|
'user_id': supabase.auth.currentUser!.id,
|
||||||
});
|
});
|
||||||
if (!ctx.mounted) return;
|
if (!mounted) return;
|
||||||
Navigator.of(ctx).pop();
|
Navigator.of(context).pop();
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text('$hwId added to your collection! 🎉')),
|
SnackBar(content: Text('${widget.hwId} added to your collection! 🎉')),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!ctx.mounted) return;
|
if (!mounted) return;
|
||||||
Navigator.of(ctx).pop();
|
setState(() => _isAdding = false);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text('Failed to add: $e'), backgroundColor: Colors.red),
|
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'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue