11import 'package:flutter/material.dart' ;
2+ import 'package:hooks_riverpod/hooks_riverpod.dart' ;
23import 'package:lucide_icons_flutter/lucide_icons.dart' ;
34
45import '../../shared/theme/theme.dart' ;
6+ import '../custom_emoji/custom_emoji.dart' ;
7+ import '../custom_emoji/custom_emoji_provider.dart' ;
8+ import '../custom_emoji/custom_emoji_render.dart' ;
59
610/// Opens the full emoji picker as a modal bottom sheet.
711void showEmojiPicker ({
@@ -185,16 +189,16 @@ const emojiCategories = <({String label, IconData icon, List<String> emoji})>[
185189 ),
186190];
187191
188- class EmojiPickerSheet extends StatefulWidget {
192+ class EmojiPickerSheet extends ConsumerStatefulWidget {
189193 final void Function (String emoji) onSelect;
190194
191195 const EmojiPickerSheet ({super .key, required this .onSelect});
192196
193197 @override
194- State <EmojiPickerSheet > createState () => _EmojiPickerSheetState ();
198+ ConsumerState <EmojiPickerSheet > createState () => _EmojiPickerSheetState ();
195199}
196200
197- class _EmojiPickerSheetState extends State <EmojiPickerSheet > {
201+ class _EmojiPickerSheetState extends ConsumerState <EmojiPickerSheet > {
198202 /// -1 = "All", 0..N = specific category.
199203 int _selectedCategory = - 1 ;
200204
@@ -210,9 +214,14 @@ class _EmojiPickerSheetState extends State<EmojiPickerSheet> {
210214 @override
211215 Widget build (BuildContext context) {
212216 final colors = Theme .of (context).colorScheme;
217+ final customEmoji = ref.watch (customEmojiListProvider);
218+ final isCustomCategory =
219+ customEmoji.isNotEmpty && _selectedCategory == emojiCategories.length;
213220 final emoji = _selectedCategory < 0
214221 ? _allEmoji
215- : emojiCategories[_selectedCategory].emoji;
222+ : _selectedCategory < emojiCategories.length
223+ ? emojiCategories[_selectedCategory].emoji
224+ : const < String > [];
216225
217226 return SizedBox (
218227 height: 340 ,
@@ -235,38 +244,88 @@ class _EmojiPickerSheetState extends State<EmojiPickerSheet> {
235244 selected: _selectedCategory == i,
236245 onTap: () => setState (() => _selectedCategory = i),
237246 ),
247+ if (customEmoji.isNotEmpty)
248+ CategoryIcon (
249+ icon: LucideIcons .sparkles,
250+ selected: isCustomCategory,
251+ onTap: () => setState (
252+ () => _selectedCategory = emojiCategories.length,
253+ ),
254+ ),
238255 ],
239256 ),
240257 ),
241258 Divider (height: 1 , color: colors.outlineVariant),
242259 const SizedBox (height: Grid .xxs),
243260 // Emoji grid.
244261 Expanded (
245- child: GridView .builder (
246- padding: const EdgeInsets .symmetric (horizontal: Grid .xs),
247- gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount (
248- crossAxisCount: 8 ,
249- mainAxisSpacing: Grid .half,
250- crossAxisSpacing: Grid .half,
251- ),
252- itemCount: emoji.length,
253- itemBuilder: (context, index) {
254- final e = emoji[index];
255- return GestureDetector (
256- onTap: () => widget.onSelect (e),
257- child: Center (
258- child: Text (e, style: const TextStyle (fontSize: 28 )),
262+ child: isCustomCategory
263+ ? _CustomEmojiGrid (
264+ emoji: customEmoji,
265+ onSelect: widget.onSelect,
266+ )
267+ : GridView .builder (
268+ padding: const EdgeInsets .symmetric (horizontal: Grid .xs),
269+ gridDelegate:
270+ const SliverGridDelegateWithFixedCrossAxisCount (
271+ crossAxisCount: 8 ,
272+ mainAxisSpacing: Grid .half,
273+ crossAxisSpacing: Grid .half,
274+ ),
275+ itemCount: emoji.length,
276+ itemBuilder: (context, index) {
277+ final e = emoji[index];
278+ return GestureDetector (
279+ onTap: () => widget.onSelect (e),
280+ child: Center (
281+ child: Text (e, style: const TextStyle (fontSize: 28 )),
282+ ),
283+ );
284+ },
259285 ),
260- );
261- },
262- ),
263286 ),
264287 ],
265288 ),
266289 );
267290 }
268291}
269292
293+ class _CustomEmojiGrid extends StatelessWidget {
294+ final List <CustomEmoji > emoji;
295+ final void Function (String emoji) onSelect;
296+
297+ const _CustomEmojiGrid ({required this .emoji, required this .onSelect});
298+
299+ @override
300+ Widget build (BuildContext context) {
301+ return GridView .builder (
302+ padding: const EdgeInsets .symmetric (horizontal: Grid .xs),
303+ gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount (
304+ crossAxisCount: 6 ,
305+ mainAxisSpacing: Grid .half,
306+ crossAxisSpacing: Grid .half,
307+ ),
308+ itemCount: emoji.length,
309+ itemBuilder: (context, index) {
310+ final entry = emoji[index];
311+ return GestureDetector (
312+ onTap: () => onSelect (':${entry .shortcode }:' ),
313+ child: Tooltip (
314+ message: ':${entry .shortcode }:' ,
315+ child: Center (
316+ child: CustomEmojiImage (
317+ shortcode: entry.shortcode,
318+ url: entry.url,
319+ size: 32 ,
320+ ),
321+ ),
322+ ),
323+ );
324+ },
325+ );
326+ }
327+ }
328+
270329class CategoryIcon extends StatelessWidget {
271330 final IconData icon;
272331 final bool selected;
0 commit comments