Main coves client
1import 'package:flutter/material.dart';
2import 'package:image_picker/image_picker.dart';
3
4import '../constants/app_colors.dart';
5
6/// A modal bottom sheet for selecting an image source (gallery or camera).
7///
8/// Usage:
9/// ```dart
10/// final source = await ImageSourcePicker.show(context);
11/// if (source != null) {
12/// // Pick image using the selected source
13/// }
14/// ```
15abstract final class ImageSourcePicker {
16 /// Shows the image source picker modal and returns the selected source.
17 ///
18 /// Returns [ImageSource.gallery], [ImageSource.camera], or null if cancelled.
19 static Future<ImageSource?> show(BuildContext context) {
20 return showModalBottomSheet<ImageSource>(
21 context: context,
22 backgroundColor: AppColors.backgroundSecondary,
23 shape: const RoundedRectangleBorder(
24 borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
25 ),
26 builder: (context) => const _ImageSourcePickerSheet(),
27 );
28 }
29}
30
31class _ImageSourcePickerSheet extends StatelessWidget {
32 const _ImageSourcePickerSheet();
33
34 @override
35 Widget build(BuildContext context) {
36 return SafeArea(
37 child: Padding(
38 padding: const EdgeInsets.symmetric(vertical: 16),
39 child: Column(
40 mainAxisSize: MainAxisSize.min,
41 children: [
42 Container(
43 width: 40,
44 height: 4,
45 margin: const EdgeInsets.only(bottom: 16),
46 decoration: BoxDecoration(
47 color: AppColors.border,
48 borderRadius: BorderRadius.circular(2),
49 ),
50 ),
51 const Text(
52 'Select Image Source',
53 style: TextStyle(
54 color: Colors.white,
55 fontSize: 18,
56 fontWeight: FontWeight.w600,
57 ),
58 ),
59 const SizedBox(height: 16),
60 ListTile(
61 leading: Container(
62 padding: const EdgeInsets.all(10),
63 decoration: BoxDecoration(
64 color: AppColors.primary.withValues(alpha: 0.1),
65 borderRadius: BorderRadius.circular(8),
66 ),
67 child: const Icon(
68 Icons.photo_library,
69 color: AppColors.primary,
70 ),
71 ),
72 title: const Text(
73 'Choose from Gallery',
74 style: TextStyle(color: Colors.white),
75 ),
76 subtitle: const Text(
77 'Select an existing photo',
78 style: TextStyle(color: AppColors.textSecondary, fontSize: 12),
79 ),
80 onTap: () => Navigator.pop(context, ImageSource.gallery),
81 ),
82 ListTile(
83 leading: Container(
84 padding: const EdgeInsets.all(10),
85 decoration: BoxDecoration(
86 color: AppColors.teal.withValues(alpha: 0.1),
87 borderRadius: BorderRadius.circular(8),
88 ),
89 child: const Icon(
90 Icons.camera_alt,
91 color: AppColors.teal,
92 ),
93 ),
94 title: const Text(
95 'Take a Photo',
96 style: TextStyle(color: Colors.white),
97 ),
98 subtitle: const Text(
99 'Use camera to capture',
100 style: TextStyle(color: AppColors.textSecondary, fontSize: 12),
101 ),
102 onTap: () => Navigator.pop(context, ImageSource.camera),
103 ),
104
105 const SizedBox(height: 8),
106 ],
107 ),
108 ),
109 );
110 }
111}