Small refactor of emoji controller and widget

Isolated deserialization of the large emoji json to not keep main thread busy to long
Refactored textController logic in EmojiPickerButton to make popping mid buid impossible. The bug from the comment should be impossible now
(The bug could be reproduce with future.delayed and microtask but)
This commit is contained in:
istalri 2026-07-25 19:04:56 +02:00
commit 0813597a53
2 changed files with 43 additions and 25 deletions

View file

@ -1,6 +1,7 @@
import "dart:convert";
import "package:emoji_text_field/models/emoji_category.dart";
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter/foundation.dart";
import "package:flutter/material.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:http/http.dart";
@ -19,9 +20,12 @@ class EmojiController extends AsyncNotifier<EmojiTuple> {
throw Exception("Failed to load emoji data");
}
final data = json.decode(response.body);
return compute(_parseEmojiJson, response.body);
}
final entries = (data as List)
static EmojiTuple _parseEmojiJson(String body) {
final data = json.decode(body) as List;
final entries = data
.cast<Map<String, dynamic>>()
.map(Emoji.fromJson)
.toIList();

View file

@ -20,32 +20,46 @@ class EmojiPickerButton extends HookConsumerWidget {
Widget build(_, WidgetRef ref) => IconButton(
onPressed: () async {
onPressed?.call();
final controller = this.controller ?? .new();
final tempController = controller ?? TextEditingController();
final shouldDispose = controller == null;
var hasPopped = false;
void handler() {
if (tempController.text.isEmpty || hasPopped) return;
hasPopped = true;
onSelection?.call(tempController.text);
WidgetsBinding.instance.addPostFrameCallback((_) {
if (context.mounted) Navigator.of(context).maybePop();
});
}
final emojis = await ref.watch(EmojiController.provider.future);
if (context.mounted) {
showModalBottomSheet(
context: context,
builder: (context) => EmojiKeyboardView(
config: .new(
showRecentTab: false,
customCategories: emojis.$1.unlock,
customKeywords: emojis.$2.unlock,
backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
height: 600,
),
textController: controller
..addListener(() {
// Without this, there will sometimes be a debugLocked is not true error sometimes
// It might be preferable to use a microtask instead of a `Future.delayed`.
Future.delayed(.zero, () {
if (context.mounted) Navigator.of(context).pop();
});
onSelection?.call(controller.text);
}),
),
);
if (!context.mounted) {
if (shouldDispose) tempController.dispose();
return;
}
await showModalBottomSheet(
context: context,
builder: (sheetContext) => EmojiKeyboardView(
config: .new(
showRecentTab: false,
customCategories: emojis.$1.unlock,
customKeywords: emojis.$2.unlock,
backgroundColor: Theme.of(
sheetContext,
).colorScheme.surfaceContainer,
height: 600,
),
textController: tempController..addListener(handler),
),
);
tempController.removeListener(handler);
if (shouldDispose) tempController.dispose();
},
icon: Icon(Icons.emoji_emotions),
);