From 457299ad816d7e41dbae4580e0bcb4e5193198d2 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Wed, 16 Sep 2026 12:22:21 -0400 Subject: [PATCH] add onSelection callback --- lib/models/emoji.dart | 9 +++++- lib/widgets/emoji_picker.dart | 61 +++++++++++++++++++++++++---------- lib/widgets/room_chat.dart | 6 +++- 3 files changed, 57 insertions(+), 19 deletions(-) diff --git a/lib/models/emoji.dart b/lib/models/emoji.dart index 0bba3cd..75faf08 100644 --- a/lib/models/emoji.dart +++ b/lib/models/emoji.dart @@ -7,12 +7,19 @@ part "emoji.g.dart"; @Freezed(toJson: false, fromJson: false) @JsonSerializable(createToJson: false) class Emoji({ - @JsonKey(fromJson: Emoji.widgetFromJson, name: "emoji") + @JsonKey( + fromJson: Emoji.widgetFromJson, + readValue: Emoji.readWidgetValueFromJson, + ) required final Widget widget, + @JsonKey(name: "emoji") required final String value, required final IList aliases, required final String description, required final IList tags, }) with _$Emoji { static Widget widgetFromJson(String emoji) => Text(emoji); + + static String readWidgetValueFromJson(Map json, _) => + json["emoji"]; factory Emoji.fromJson(Map json) => _$EmojiFromJson(json); } diff --git a/lib/widgets/emoji_picker.dart b/lib/widgets/emoji_picker.dart index 079eae5..d1248b5 100644 --- a/lib/widgets/emoji_picker.dart +++ b/lib/widgets/emoji_picker.dart @@ -1,3 +1,5 @@ +import "dart:async"; + import "package:collection/collection.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter_hooks/flutter_hooks.dart"; @@ -11,14 +13,19 @@ import "package:super_sliver_list/super_sliver_list.dart"; final class const EmojiPicker({ final IList extraCategories = const IList.empty(), + required final FutureOr Function(String value) onSelection, super.key, }) extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final selectedCategory = useState(null); + final scrollController = useScrollController(); final listController = useMemoized(ListController.new); + final categoryScrollController = useScrollController(); + final categoryListController = useMemoized(ListController.new); + useEffect(() { void onChange() { final range = listController.visibleRange; @@ -27,6 +34,13 @@ final class const EmojiPicker({ if (selectedCategory.value != firstVisible) { WidgetsBinding.instance.addPostFrameCallback((_) { selectedCategory.value = firstVisible; + categoryListController.animateToItem( + index: firstVisible, + scrollController: categoryScrollController, + alignment: 0, + duration: (_) => Duration(milliseconds: 300), + curve: (_) => Curves.easeInOut, + ); }); } } @@ -43,7 +57,9 @@ final class const EmojiPicker({ .watch(EmojiController.provider) .betterWhen( data: (categories) { - final combined = categories.addAll(extraCategories); + final combined = useMemoized( + () => categories.addAll(extraCategories), + ); return Column( spacing: 4, @@ -56,8 +72,10 @@ final class const EmojiPicker({ ), SizedBox( height: 48, - child: ListView( + child: SuperListView( scrollDirection: .horizontal, + controller: categoryScrollController, + listController: categoryListController, children: combined .mapIndexed( (index, category) => Padding( @@ -97,21 +115,30 @@ final class const EmojiPicker({ crossAxisAlignment: CrossAxisAlignment.start, children: [ DividerText(category.name), - Wrap( - children: category.emojis - .map( - (emoji) => IconButton( - onPressed: () {}, - icon: SizedBox.square( - dimension: 36, - child: FittedBox( - fit: .contain, - child: emoji.widget, - ), - ), - ), - ) - .toList(), + GridView.builder( + gridDelegate: + const SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 56, + mainAxisExtent: 56, + ), + itemCount: category.emojis.length, + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + final emoji = category.emojis[index]; + + return IconButton( + padding: EdgeInsets.zero, + onPressed: () async { + Navigator.of(context).pop(); + await onSelection(emoji.value); + }, + icon: SizedBox.square( + dimension: 36, + child: FittedBox(child: emoji.widget), + ), + ); + }, ), ], ); diff --git a/lib/widgets/room_chat.dart b/lib/widgets/room_chat.dart index 528d47e..812daf0 100644 --- a/lib/widgets/room_chat.dart +++ b/lib/widgets/room_chat.dart @@ -244,7 +244,11 @@ final class const RoomChat({ showDragHandle: true, isScrollControlled: true, context: context, - builder: (context) => EmojiPicker(), + builder: (context) => EmojiPicker( + onSelection: (value) => notifier + .sendReaction(value, event) + .onError(showError), + ), ); }, ),