Emoji-Picker-App in React Native
Emoji-Picker-App
~11 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026
Eine Emoji-Auswahl (wie in Chat-Apps) verbindet Kategorien-Tabs, ein gefiltertes Raster und einen "zuletzt verwendet"-Verlauf – ein kompaktes, aber vollständiges Beispiel für kategorisierte Rasteransichten.
1. Beschreibung
Emojis sind ganz normale Unicode-ZEICHEN, keine Bilder – sie werden als Text gerendert wie jeder andere String. Die App organisiert eine feste Emoji-Liste nach Kategorie, zeigt sie in einem Raster (numColumns) und merkt sich kürzlich ausgewählte Emojis in einer separaten Liste.
2. Kurzes Beispiel
<TouchableOpacity onPress={() => onAuswahl('????')}>
<Text style={{ fontSize: 28 }}>????</Text>
</TouchableOpacity>3. Komplettes Projekt: Emoji-Picker mit Verlauf
npx create-expo-app emoji-picker-app
cd emoji-picker-appexport const EMOJI_KATEGORIEN = [
{
name: 'Smileys',
emojis: ['????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????'],
},
{
name: 'Tiere',
emojis: ['????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????'],
},
{
name: 'Essen',
emojis: ['????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????'],
},
];import { useState } from 'react';
import { View, Text, TouchableOpacity, FlatList, StyleSheet } from 'react-native';
import { EMOJI_KATEGORIEN } from './daten/emojis';
const MAX_VERLAUF = 12;
export default function App() {
const [kategorieIndex, setKategorieIndex] = useState(0);
const [ausgewaehlt, setAusgewaehlt] = useState(null);
const [verlauf, setVerlauf] = useState([]);
function emojiAuswaehlen(emoji) {
setAusgewaehlt(emoji);
setVerlauf((liste) => {
const ohneDuplikat = liste.filter((e) => e !== emoji);
return [emoji, ...ohneDuplikat].slice(0, MAX_VERLAUF);
});
}
const aktuelleKategorie = EMOJI_KATEGORIEN[kategorieIndex];
return (
<View style={styles.container}>
<View style={styles.vorschau}>
<Text style={styles.vorschauEmoji}>{ausgewaehlt ?? '❔'}</Text>
<Text style={styles.vorschauText}>
{ausgewaehlt ? 'Ausgewähltes Emoji' : 'Noch nichts ausgewählt'}
</Text>
</View>
{verlauf.length > 0 && (
<View style={styles.verlaufBereich}>
<Text style={styles.verlaufTitel}>Zuletzt verwendet</Text>
<FlatList
horizontal
data={verlauf}
keyExtractor={(item, index) => `${item}-${index}`}
showsHorizontalScrollIndicator={false}
renderItem={({ item }) => (
<TouchableOpacity onPress={() => emojiAuswaehlen(item)} style={styles.verlaufZelle}>
<Text style={styles.emojiText}>{item}</Text>
</TouchableOpacity>
)}
/>
</View>
)}
<View style={styles.tabZeile}>
{EMOJI_KATEGORIEN.map((kategorie, index) => (
<TouchableOpacity
key={kategorie.name}
style={[styles.tab, index === kategorieIndex && styles.tabAktiv]}
onPress={() => setKategorieIndex(index)}
>
<Text style={[styles.tabText, index === kategorieIndex && styles.tabTextAktiv]}>
{kategorie.name}
</Text>
</TouchableOpacity>
))}
</View>
<FlatList
key={kategorieIndex}
data={aktuelleKategorie.emojis}
numColumns={6}
keyExtractor={(item) => item}
renderItem={({ item }) => (
<TouchableOpacity style={styles.rasterZelle} onPress={() => emojiAuswaehlen(item)}>
<Text style={styles.emojiText}>{item}</Text>
</TouchableOpacity>
)}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, paddingTop: 60, paddingHorizontal: 16 },
vorschau: { alignItems: 'center', marginBottom: 16 },
vorschauEmoji: { fontSize: 48 },
vorschauText: { color: '#6b7280', marginTop: 4 },
verlaufBereich: { marginBottom: 16 },
verlaufTitel: { color: '#6b7280', marginBottom: 6, fontSize: 13 },
verlaufZelle: { marginRight: 12 },
tabZeile: { flexDirection: 'row', marginBottom: 8 },
tab: { flex: 1, paddingVertical: 8, borderBottomWidth: 2, borderBottomColor: 'transparent', alignItems: 'center' },
tabAktiv: { borderBottomColor: '#2563eb' },
tabText: { color: '#6b7280' },
tabTextAktiv: { color: '#2563eb', fontWeight: 'bold' },
rasterZelle: { flex: 1, aspectRatio: 1, alignItems: 'center', justifyContent: 'center' },
emojiText: { fontSize: 28 },
});4. Erklärung
verlaufnutztfilter+slice(0, MAX_VERLAUF)statt einfachem Anhängen – so wandert ein BEREITS im Verlauf vorhandenes Emoji beim erneuten Antippen an den ANFANG statt sich zu duplizieren, und die Liste wächst nie über 12 Einträge hinaus.key={{kategorieIndex}}auf der Raster-FlatListzwingt React, die Liste beim Kategoriewechsel komplett neu aufzubauen – wichtig, damit die Scroll-Position beim Wechseln der Kategorie zurückgesetzt wird.- Die horizontale Verlaufsliste (
horizontalaufFlatList) und das vertikale Emoji-Raster (numColumns={{6}}) sind zwei UNTERSCHIEDLICHE Konfigurationen DERSELBEN Komponente – zeigt, wie flexibelFlatListfür verschiedene Layouts einsetzbar ist. - Emojis als Text zu rendern hat den Vorteil, dass Größe/Farbe wie bei jedem anderen Text über
fontSizegesteuert werden – kein Bild-Loading, keine Netzwerk-Anfrage nötig.