fix performance issues with new emoji picker

This commit is contained in:
Henry Hiles 2026-09-16 11:41:11 -04:00
commit 3af38e615e
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs

View file

@ -14,118 +14,114 @@ final class const EmojiPicker({
super.key, super.key,
}) extends HookConsumerWidget { }) extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) => Container( Widget build(BuildContext context, WidgetRef ref) {
padding: .symmetric(horizontal: 16), final selectedCategory = useState<int?>(null);
constraints: .loose(.new(500, 700)), final scrollController = useScrollController();
child: ref final listController = useMemoized(ListController.new);
.watch(EmojiController.provider)
.betterWhen(
data: (categories) {
final combined = categories.addAll(extraCategories);
return HookBuilder( useEffect(() {
builder: (context) { void onChange() {
final selectedCategory = useState<int?>(null); final range = listController.visibleRange;
final scrollController = useScrollController(); if (range != null) {
final listController = useMemoized(ListController.new); final firstVisible = range.$1;
if (selectedCategory.value != firstVisible) {
WidgetsBinding.instance.addPostFrameCallback((_) {
selectedCategory.value = firstVisible;
});
}
}
}
useEffect(() { listController.addListener(onChange);
void onChange() { return () => listController.removeListener(onChange);
final range = listController.visibleRange; }, [listController]);
if (range != null) {
final firstVisible = range.$1;
if (selectedCategory.value != firstVisible) {
WidgetsBinding.instance.addPostFrameCallback((_) {
selectedCategory.value = firstVisible;
});
}
}
}
listController.addListener(onChange); return Container(
return () => listController.removeListener(onChange); padding: .symmetric(horizontal: 16),
}, [listController]); constraints: .loose(.new(500, 700)),
child: ref
.watch(EmojiController.provider)
.betterWhen(
data: (categories) {
final combined = categories.addAll(extraCategories);
return Column( return Column(
spacing: 4, spacing: 4,
children: [ children: [
SearchAnchor.bar( SearchAnchor.bar(
barHintText: "Search emoji", barHintText: "Search emoji",
suggestionsBuilder: (context, controller) { suggestionsBuilder: (context, controller) {
return []; return [];
},
),
SizedBox(
height: 48,
child: ListView(
scrollDirection: .horizontal,
children: combined
.mapIndexed(
(index, category) => Padding(
padding: .symmetric(horizontal: 4),
child: FilterChip(
label: Text(category.name),
avatar: category.icon,
selected: selectedCategory.value == index,
showCheckmark: false,
onSelected: (_) {
selectedCategory.value = index;
listController.animateToItem(
index: index,
scrollController: scrollController,
curve: (_) => Curves.easeInOut,
duration: (_) =>
Duration(milliseconds: 300),
alignment: 0,
);
},
),
),
)
.toList(),
),
),
Expanded(
child: SuperListView.builder(
listController: listController,
controller: scrollController,
itemCount: combined.length,
itemBuilder: (context, index) {
final category = combined[index];
return Column(
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(),
),
],
);
}, },
), ),
SizedBox( ),
height: 48, ],
child: ListView( );
scrollDirection: .horizontal, },
children: combined ),
.mapIndexed( );
(index, category) => Padding( }
padding: .symmetric(horizontal: 4),
child: FilterChip(
label: Text(category.name),
avatar: category.icon,
selected: selectedCategory.value == index,
showCheckmark: false,
onSelected: (_) {
selectedCategory.value = index;
listController.animateToItem(
index: index,
scrollController: scrollController,
curve: (_) => Curves.easeInOut,
duration: (_) =>
Duration(milliseconds: 300),
alignment: 0,
);
},
),
),
)
.toList(),
),
),
Expanded(
child: SuperListView.builder(
listController: listController,
controller: scrollController,
itemCount: combined.length,
itemBuilder: (context, index) {
final category = combined[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DividerText(category.name),
DefaultTextStyle(
style: TextStyle(fontSize: 24),
child: GridView(
gridDelegate:
const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 52,
mainAxisExtent: 52,
),
children: category.emojis
.map(
(emoji) => IconButton(
onPressed: () {},
icon: emoji.widget,
),
)
.toList(),
),
),
],
);
},
),
),
],
);
},
);
},
),
);
} }