main
dart 220 lines 7.34 KB
Raw
1 import 'package:flutter/material.dart';
2 import 'package:hugeicons/hugeicons.dart';
3
4 import 'package:localmind/core/theme/colors.dart';
5 import 'package:localmind/l10n/app_localizations.dart';
6
7 /// Shows the modern, grid-style attachment picker bottom sheet.
8 ///
9 /// Returns a result the caller can act on, or `null` if the user dismissed
10 /// the sheet. Designed to mirror the clean action-sheet pattern used in
11 /// iOS / Android system pickers — large rounded card, three short tile
12 /// actions, drag handle, and tappable outside-to-dismiss.
13 enum AttachAction { documents, images, savedMessage }
14
15 Future<AttachAction?> showAttachSheet(BuildContext context) {
16 return showModalBottomSheet<AttachAction>(
17 context: context,
18 backgroundColor: Colors.transparent,
19 barrierColor: Colors.black.withValues(alpha: 0.45),
20 isScrollControlled: true,
21 showDragHandle: false,
22 useSafeArea: true,
23 builder: (_) => const _AttachSheet(),
24 );
25 }
26
27 class _AttachSheet extends StatelessWidget {
28 const _AttachSheet();
29
30 @override
31 Widget build(BuildContext context) {
32 final l10n = AppLocalizations.of(context)!;
33 final theme = Theme.of(context);
34 final isDark = theme.brightness == Brightness.dark;
35
36 final bg = isDark ? AppColors.darkSurface : AppColors.lightSurface;
37 final tileBg = isDark
38 ? AppColors.darkSurfaceInput
39 : theme.colorScheme.surfaceContainerHighest.withValues(alpha: 0.45);
40 final fg = isDark ? AppColors.darkPrimaryText : AppColors.lightPrimaryText;
41 final mutedFg = isDark ? AppColors.darkMutedText : AppColors.lightMutedText;
42
43 return Padding(
44 padding: const EdgeInsets.fromLTRB(12, 0, 12, 12),
45 child: Container(
46 decoration: BoxDecoration(
47 color: bg,
48 borderRadius: BorderRadius.circular(24),
49 boxShadow: [
50 BoxShadow(
51 color: Colors.black.withValues(alpha: isDark ? 0.4 : 0.12),
52 blurRadius: 30,
53 offset: const Offset(0, 8),
54 ),
55 ],
56 ),
57 child: SafeArea(
58 top: false,
59 child: Column(
60 mainAxisSize: MainAxisSize.min,
61 children: [
62 const SizedBox(height: 8),
63 Container(
64 width: 40,
65 height: 4,
66 decoration: BoxDecoration(
67 color: mutedFg.withValues(alpha: 0.4),
68 borderRadius: BorderRadius.circular(2),
69 ),
70 ),
71 const SizedBox(height: 16),
72 Padding(
73 padding: const EdgeInsets.symmetric(horizontal: 20),
74 child: Row(
75 children: [
76 Expanded(
77 child: Column(
78 crossAxisAlignment: CrossAxisAlignment.start,
79 children: [
80 Text(
81 l10n.add_to_chat,
82 style: TextStyle(
83 fontSize: 17,
84 fontWeight: FontWeight.w700,
85 color: fg,
86 ),
87 ),
88 const SizedBox(height: 2),
89 Text(
90 l10n.choose_attachment_subtitle,
91 style: TextStyle(fontSize: 13, color: mutedFg),
92 ),
93 ],
94 ),
95 ),
96 IconButton(
97 icon: HugeIcon(
98 icon: HugeIcons.strokeRoundedCancel01,
99 color: mutedFg,
100 size: 20,
101 ),
102 onPressed: () => Navigator.of(context).pop(),
103 ),
104 ],
105 ),
106 ),
107 const SizedBox(height: 8),
108 Padding(
109 padding: const EdgeInsets.fromLTRB(16, 8, 16, 20),
110 child: Row(
111 children: [
112 Expanded(
113 child: _AttachTile(
114 icon: HugeIcons.strokeRoundedImage01,
115 label: l10n.attach_shortcut_images,
116 tileColor: tileBg,
117 foreground: fg,
118 accent: const Color(0xFF7C5CFF),
119 onTap: () =>
120 Navigator.of(context).pop(AttachAction.images),
121 ),
122 ),
123 const SizedBox(width: 10),
124 Expanded(
125 child: _AttachTile(
126 icon: HugeIcons.strokeRoundedFile01,
127 label: l10n.attach_shortcut_documents,
128 tileColor: tileBg,
129 foreground: fg,
130 accent: const Color(0xFFFF8A4C),
131 onTap: () =>
132 Navigator.of(context).pop(AttachAction.documents),
133 ),
134 ),
135 const SizedBox(width: 10),
136 Expanded(
137 child: _AttachTile(
138 icon: HugeIcons.strokeRoundedBookmark01,
139 label: l10n.attach_shortcut_saved,
140 tileColor: tileBg,
141 foreground: fg,
142 accent: const Color(0xFF22C55E),
143 onTap: () => Navigator.of(
144 context,
145 ).pop(AttachAction.savedMessage),
146 ),
147 ),
148 ],
149 ),
150 ),
151 ],
152 ),
153 ),
154 ),
155 );
156 }
157 }
158
159 class _AttachTile extends StatelessWidget {
160 const _AttachTile({
161 required this.icon,
162 required this.label,
163 required this.tileColor,
164 required this.foreground,
165 required this.accent,
166 required this.onTap,
167 });
168
169 final dynamic icon;
170 final String label;
171 final Color tileColor;
172 final Color foreground;
173 final Color accent;
174 final VoidCallback onTap;
175
176 @override
177 Widget build(BuildContext context) {
178 return Material(
179 color: Colors.transparent,
180 child: InkWell(
181 onTap: onTap,
182 borderRadius: BorderRadius.circular(16),
183 child: Container(
184 padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 8),
185 decoration: BoxDecoration(
186 color: tileColor,
187 borderRadius: BorderRadius.circular(16),
188 ),
189 child: Column(
190 mainAxisSize: MainAxisSize.min,
191 children: [
192 Container(
193 width: 40,
194 height: 40,
195 decoration: BoxDecoration(
196 color: accent.withValues(alpha: 0.14),
197 borderRadius: BorderRadius.circular(12),
198 ),
199 child: Center(
200 child: HugeIcon(icon: icon, color: accent, size: 22),
201 ),
202 ),
203 const SizedBox(height: 8),
204 Text(
205 label,
206 maxLines: 1,
207 overflow: TextOverflow.ellipsis,
208 style: TextStyle(
209 fontSize: 12,
210 fontWeight: FontWeight.w600,
211 color: foreground,
212 ),
213 ),
214 ],
215 ),
216 ),
217 ),
218 );
219 }
220 }