make grid more flexible

This commit is contained in:
Henry Hiles 2026-10-23 17:11:05 -04:00
commit 97c4324c8f
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs

View file

@ -15,17 +15,30 @@ class EmojiPicker extends HookConsumerWidget {
this.appendCategories = const IList.empty(), this.appendCategories = const IList.empty(),
required this.onSelection, required this.onSelection,
this.onFreeText, this.onFreeText,
this.includeDefaultEmoji = true,
this.itemSize = 56,
this.searchHint = "Search emoji",
super.key, super.key,
}); });
final IList<EmojiCategory> prependCategories; final IList<EmojiCategory> prependCategories;
final IList<EmojiCategory> appendCategories; final IList<EmojiCategory> appendCategories;
/// Called with the emoji that was selected. /// Called with the emoji that was selected.
final FutureOr<void> Function(Emoji emoji) onSelection; final FutureOr<void> Function(Emoji emoji) onSelection;
/// If set, the search text can be submitted as is, calling this with it. /// If set, the search text can be submitted as is, calling this with it.
final FutureOr<void> Function(String text)? onFreeText; final FutureOr<void> Function(String text)? onFreeText;
/// Whether to show the default unicode emoji after [prependCategories],
/// e.g. false for a sticker picker of only custom categories.
final bool includeDefaultEmoji;
/// The width and height of each item in the grid.
final double itemSize;
final String searchHint;
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final selectedCategory = useState<int>(0); final selectedCategory = useState<int>(0);
@ -39,7 +52,9 @@ class EmojiPicker extends HookConsumerWidget {
return Container( return Container(
padding: .all(16), padding: .all(16),
constraints: .loose(.new(500, 700)), constraints: .loose(.new(500, 700)),
child: switch (ref.watch(EmojiController.provider)) { child: switch (includeDefaultEmoji
? ref.watch(EmojiController.provider)
: const AsyncData(IList<EmojiCategory>.empty())) {
AsyncError(:final error) => throw error, AsyncError(:final error) => throw error,
AsyncLoading _ => Center(child: CircularProgressIndicator()), AsyncLoading _ => Center(child: CircularProgressIndicator()),
AsyncData(value: final categories) => HookBuilder( AsyncData(value: final categories) => HookBuilder(
@ -47,6 +62,7 @@ class EmojiPicker extends HookConsumerWidget {
final combined = useMemoized( final combined = useMemoized(
() => () =>
prependCategories.addAll(categories).addAll(appendCategories), prependCategories.addAll(categories).addAll(appendCategories),
[prependCategories, categories, appendCategories],
); );
final headerKeys = useMemoized( final headerKeys = useMemoized(
@ -106,7 +122,7 @@ class EmojiPicker extends HookConsumerWidget {
return Column( return Column(
children: [ children: [
SearchBar( SearchBar(
hintText: "Search emoji", hintText: searchHint,
padding: const WidgetStatePropertyAll<EdgeInsets>( padding: const WidgetStatePropertyAll<EdgeInsets>(
.symmetric(horizontal: 16.0), .symmetric(horizontal: 16.0),
), ),
@ -207,9 +223,9 @@ class EmojiPicker extends HookConsumerWidget {
), ),
SliverGrid.builder( SliverGrid.builder(
gridDelegate: gridDelegate:
const SliverGridDelegateWithMaxCrossAxisExtent( SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 56, maxCrossAxisExtent: itemSize,
mainAxisExtent: 56, mainAxisExtent: itemSize,
), ),
itemCount: emojis.length, itemCount: emojis.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
@ -217,10 +233,9 @@ class EmojiPicker extends HookConsumerWidget {
return IconButton( return IconButton(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
onPressed: () => onPressed: () => onSelection(emoji),
onSelection(emoji),
icon: SizedBox.square( icon: SizedBox.square(
dimension: 36, dimension: itemSize * 9 / 14,
child: FittedBox(child: emoji.widget), child: FittedBox(child: emoji.widget),
), ),
); );