2025-12-30 20:04:53 -07:00
|
|
|
import 'package:flutter/material.dart';
|
2026-01-11 17:13:50 -07:00
|
|
|
import '../l10n/app_localizations.dart';
|
|
|
|
|
import '../l10n/l10n.dart';
|
2025-12-30 20:04:53 -07:00
|
|
|
|
|
|
|
|
class EmojiPicker extends StatelessWidget {
|
|
|
|
|
final Function(String) onEmojiSelected;
|
|
|
|
|
|
2026-02-04 08:32:35 -08:00
|
|
|
const EmojiPicker({super.key, required this.onEmojiSelected});
|
2025-12-30 20:04:53 -07:00
|
|
|
|
|
|
|
|
static const List<String> quickEmojis = ['👍', '❤️', '😂', '🎉', '👏', '🔥'];
|
|
|
|
|
|
2026-01-28 23:21:04 -07:00
|
|
|
static const List<String> smileys = [
|
2026-02-04 08:32:35 -08:00
|
|
|
'😀',
|
|
|
|
|
'😃',
|
|
|
|
|
'😄',
|
|
|
|
|
'😁',
|
|
|
|
|
'😅',
|
|
|
|
|
'😂',
|
|
|
|
|
'🤣',
|
|
|
|
|
'😊',
|
|
|
|
|
'😇',
|
|
|
|
|
'🙂',
|
|
|
|
|
'🙃',
|
|
|
|
|
'😉',
|
|
|
|
|
'😌',
|
|
|
|
|
'😍',
|
|
|
|
|
'🥰',
|
|
|
|
|
'😘',
|
|
|
|
|
'😗',
|
|
|
|
|
'😙',
|
|
|
|
|
'😚',
|
|
|
|
|
'😋',
|
|
|
|
|
'😛',
|
|
|
|
|
'😝',
|
|
|
|
|
'😜',
|
|
|
|
|
'🤪',
|
|
|
|
|
'🤨',
|
|
|
|
|
'🧐',
|
|
|
|
|
'🤓',
|
|
|
|
|
'😎',
|
|
|
|
|
'🥸',
|
|
|
|
|
'🤩',
|
|
|
|
|
'🥳',
|
|
|
|
|
'😏',
|
|
|
|
|
'😒',
|
|
|
|
|
'😞',
|
|
|
|
|
'😔',
|
|
|
|
|
'😟',
|
|
|
|
|
'😕',
|
|
|
|
|
'🙁',
|
|
|
|
|
'😣',
|
|
|
|
|
'😖',
|
|
|
|
|
'😫',
|
|
|
|
|
'😩',
|
|
|
|
|
'🥺',
|
|
|
|
|
'😢',
|
|
|
|
|
'😭',
|
|
|
|
|
'😤',
|
|
|
|
|
'😠',
|
|
|
|
|
'😡',
|
|
|
|
|
'🤬',
|
|
|
|
|
'🤯',
|
|
|
|
|
'😳',
|
|
|
|
|
'🥵',
|
|
|
|
|
'🥶',
|
|
|
|
|
'😱',
|
|
|
|
|
'😨',
|
|
|
|
|
'😰',
|
|
|
|
|
'😥',
|
|
|
|
|
'😓',
|
|
|
|
|
'🤗',
|
|
|
|
|
'🤔',
|
|
|
|
|
'🤭',
|
|
|
|
|
'🤫',
|
|
|
|
|
'🤥',
|
|
|
|
|
'😶',
|
|
|
|
|
];
|
2026-01-28 23:21:04 -07:00
|
|
|
static const List<String> gestures = [
|
2026-02-04 08:32:35 -08:00
|
|
|
'👍',
|
|
|
|
|
'👎',
|
|
|
|
|
'👊',
|
|
|
|
|
'✊',
|
|
|
|
|
'🤛',
|
|
|
|
|
'🤜',
|
|
|
|
|
'🤞',
|
|
|
|
|
'✌️',
|
|
|
|
|
'🤟',
|
|
|
|
|
'🤘',
|
|
|
|
|
'👌',
|
|
|
|
|
'🤌',
|
|
|
|
|
'🤏',
|
|
|
|
|
'👈',
|
|
|
|
|
'👉',
|
|
|
|
|
'👆',
|
|
|
|
|
'👇',
|
|
|
|
|
'☝️',
|
|
|
|
|
'👋',
|
|
|
|
|
'🤚',
|
|
|
|
|
'🖐️',
|
|
|
|
|
'✋',
|
|
|
|
|
'🖖',
|
|
|
|
|
'👏',
|
|
|
|
|
'🙌',
|
|
|
|
|
'👐',
|
|
|
|
|
'🤲',
|
|
|
|
|
'🤝',
|
|
|
|
|
'🙏',
|
|
|
|
|
'✍️',
|
|
|
|
|
'💅',
|
|
|
|
|
'🤳',
|
|
|
|
|
'💪',
|
|
|
|
|
];
|
2026-01-28 23:21:04 -07:00
|
|
|
static const List<String> hearts = [
|
2026-02-04 08:32:35 -08:00
|
|
|
'❤️',
|
|
|
|
|
'🧡',
|
|
|
|
|
'💛',
|
|
|
|
|
'💚',
|
|
|
|
|
'💙',
|
|
|
|
|
'💜',
|
|
|
|
|
'🖤',
|
|
|
|
|
'🤍',
|
|
|
|
|
'🤎',
|
|
|
|
|
'💔',
|
|
|
|
|
'❤️🔥',
|
|
|
|
|
'❤️🩹',
|
|
|
|
|
'💕',
|
|
|
|
|
'💞',
|
|
|
|
|
'💓',
|
|
|
|
|
'💗',
|
|
|
|
|
'💖',
|
|
|
|
|
'💘',
|
|
|
|
|
'💝',
|
|
|
|
|
'💟',
|
|
|
|
|
'💌',
|
|
|
|
|
'💢',
|
|
|
|
|
'💥',
|
|
|
|
|
'💫',
|
|
|
|
|
'💦',
|
|
|
|
|
'💨',
|
|
|
|
|
'🕳️',
|
|
|
|
|
'💬',
|
|
|
|
|
'👁️🗨️',
|
|
|
|
|
'🗨️',
|
|
|
|
|
'🗯️',
|
|
|
|
|
'💭',
|
|
|
|
|
];
|
2026-01-28 23:21:04 -07:00
|
|
|
static const List<String> objects = [
|
2026-02-04 08:32:35 -08:00
|
|
|
'🎉',
|
|
|
|
|
'🎊',
|
|
|
|
|
'🎈',
|
|
|
|
|
'🎁',
|
|
|
|
|
'🎀',
|
|
|
|
|
'🪅',
|
|
|
|
|
'🪆',
|
|
|
|
|
'🏆',
|
|
|
|
|
'🥇',
|
|
|
|
|
'🥈',
|
|
|
|
|
'🥉',
|
|
|
|
|
'⚽',
|
|
|
|
|
'⚾',
|
|
|
|
|
'🥎',
|
|
|
|
|
'🏀',
|
|
|
|
|
'🏐',
|
|
|
|
|
'🏈',
|
|
|
|
|
'🏉',
|
|
|
|
|
'🎾',
|
|
|
|
|
'🥏',
|
|
|
|
|
'🎳',
|
|
|
|
|
'🏏',
|
|
|
|
|
'🏑',
|
|
|
|
|
'🏒',
|
|
|
|
|
'🥍',
|
|
|
|
|
'🏓',
|
|
|
|
|
'🏸',
|
|
|
|
|
'🥊',
|
|
|
|
|
'🥋',
|
|
|
|
|
'🥅',
|
|
|
|
|
'⛳',
|
|
|
|
|
'🔥',
|
|
|
|
|
'⭐',
|
|
|
|
|
'🌟',
|
|
|
|
|
'✨',
|
|
|
|
|
'⚡',
|
|
|
|
|
'💡',
|
|
|
|
|
'🔦',
|
|
|
|
|
'🏮',
|
|
|
|
|
'🪔',
|
|
|
|
|
'📱',
|
|
|
|
|
'💻',
|
|
|
|
|
'⌚',
|
|
|
|
|
'📷',
|
|
|
|
|
'📺',
|
|
|
|
|
'📻',
|
|
|
|
|
'🎵',
|
|
|
|
|
'🎶',
|
|
|
|
|
'🚀',
|
|
|
|
|
];
|
2026-01-11 17:13:50 -07:00
|
|
|
|
|
|
|
|
Map<String, List<String>> _emojiCategories(AppLocalizations l10n) {
|
|
|
|
|
return {
|
2026-01-28 23:21:04 -07:00
|
|
|
l10n.emojiCategorySmileys: smileys,
|
|
|
|
|
l10n.emojiCategoryGestures: gestures,
|
|
|
|
|
l10n.emojiCategoryHearts: hearts,
|
|
|
|
|
l10n.emojiCategoryObjects: objects,
|
2026-01-11 17:13:50 -07:00
|
|
|
};
|
|
|
|
|
}
|
2025-12-30 20:04:53 -07:00
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
2026-01-11 17:13:50 -07:00
|
|
|
final l10n = context.l10n;
|
|
|
|
|
final emojiCategories = _emojiCategories(l10n);
|
2025-12-30 20:04:53 -07:00
|
|
|
return Container(
|
|
|
|
|
height: MediaQuery.of(context).size.height * 0.5,
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
color: Theme.of(context).colorScheme.surface,
|
|
|
|
|
borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
|
|
|
|
|
),
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
Padding(
|
|
|
|
|
padding: const EdgeInsets.all(16),
|
|
|
|
|
child: Row(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
|
children: [
|
2026-01-11 17:13:50 -07:00
|
|
|
Text(
|
|
|
|
|
l10n.chat_addReaction,
|
2026-02-04 08:32:35 -08:00
|
|
|
style: const TextStyle(
|
|
|
|
|
fontSize: 18,
|
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
|
),
|
2025-12-30 20:04:53 -07:00
|
|
|
),
|
|
|
|
|
IconButton(
|
|
|
|
|
icon: const Icon(Icons.close),
|
|
|
|
|
onPressed: () => Navigator.pop(context),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
Padding(
|
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
|
|
|
child: Wrap(
|
|
|
|
|
spacing: 12,
|
|
|
|
|
children: quickEmojis
|
|
|
|
|
.map(
|
|
|
|
|
(emoji) => InkWell(
|
|
|
|
|
onTap: () {
|
|
|
|
|
onEmojiSelected(emoji);
|
|
|
|
|
Navigator.pop(context);
|
|
|
|
|
},
|
|
|
|
|
child: Container(
|
|
|
|
|
padding: const EdgeInsets.all(8),
|
|
|
|
|
decoration: BoxDecoration(
|
2026-02-04 08:32:35 -08:00
|
|
|
color: Theme.of(
|
|
|
|
|
context,
|
|
|
|
|
).colorScheme.secondaryContainer,
|
2025-12-30 20:04:53 -07:00
|
|
|
borderRadius: BorderRadius.circular(8),
|
|
|
|
|
),
|
|
|
|
|
child: Text(
|
|
|
|
|
emoji,
|
|
|
|
|
style: const TextStyle(fontSize: 28),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.toList(),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
const Divider(),
|
|
|
|
|
Expanded(
|
|
|
|
|
child: DefaultTabController(
|
|
|
|
|
length: emojiCategories.length,
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
TabBar(
|
|
|
|
|
isScrollable: true,
|
|
|
|
|
tabs: emojiCategories.keys
|
|
|
|
|
.map((cat) => Tab(text: cat))
|
|
|
|
|
.toList(),
|
|
|
|
|
),
|
|
|
|
|
Expanded(
|
|
|
|
|
child: TabBarView(
|
|
|
|
|
children: emojiCategories.values
|
|
|
|
|
.map(
|
|
|
|
|
(emojis) => GridView.builder(
|
|
|
|
|
padding: const EdgeInsets.all(8),
|
2026-02-04 08:32:35 -08:00
|
|
|
gridDelegate:
|
|
|
|
|
const SliverGridDelegateWithFixedCrossAxisCount(
|
|
|
|
|
crossAxisCount: 8,
|
|
|
|
|
mainAxisSpacing: 8,
|
|
|
|
|
crossAxisSpacing: 8,
|
|
|
|
|
),
|
2025-12-30 20:04:53 -07:00
|
|
|
itemCount: emojis.length,
|
|
|
|
|
itemBuilder: (context, index) => InkWell(
|
|
|
|
|
onTap: () {
|
|
|
|
|
onEmojiSelected(emojis[index]);
|
|
|
|
|
Navigator.pop(context);
|
|
|
|
|
},
|
|
|
|
|
child: Center(
|
|
|
|
|
child: Text(
|
|
|
|
|
emojis[index],
|
|
|
|
|
style: const TextStyle(fontSize: 28),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.toList(),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|