RN Lezione 7: FlatList e TouchableOpacity
Obiettivi
Section titled “Obiettivi”- Usare FlatList per liste performanti
- Usare TouchableOpacity per elementi cliccabili
- Gestire eventi long-press
- Aggiungere e rimuovere elementi da una lista
1. FlatList — Liste performanti
Section titled “1. FlatList — Liste performanti”FlatList renderizza solo gli elementi visibili (virtualizzazione). È MOLTO meglio di un array di View dentro ScrollView per liste lunghe.
import { FlatList, View, Text, StyleSheet } from 'react-native';
const DATA = [ { id: '1', nome: 'Mario', eta: 17 }, { id: '2', nome: 'Sofia', eta: 16 }, { id: '3', nome: 'Luca', eta: 17 },];
export default function App() { const renderItem = ({ item }) => ( // ↑ item = elemento corrente <View style={styles.item}> <Text style={styles.nome}>{item.nome}</Text> <Text style={styles.eta}>{item.eta} anni</Text> </View> );
return ( <FlatList data={DATA} // Array di dati renderItem={renderItem} // Come renderizzare keyExtractor={item => item.id} // Chiave unica per ogni item /> );}Confronto: ScrollView vs FlatList
Section titled “Confronto: ScrollView vs FlatList”ScrollView: [item1][item2][item3]...[item100] ← TUTTI renderizzatiFlatList: [item1][item2][item3] ← SOLO quelli visibili [item4] ← quando scrolliPer liste con +20 elementi, usa sempre FlatList!
Proprietà FlatList
Section titled “Proprietà FlatList”| Proprietà | Descrizione |
|---|---|
data | Array di dati |
renderItem | ({ item, index, separators }) => JSX |
keyExtractor | Estrae la chiave unica (item => item.id) |
ListHeaderComponent | Componente in testa |
ListFooterComponent | Componente in fondo |
ListEmptyComponent | Componente se lista vuota |
ItemSeparatorComponent | Separatore tra elementi |
horizontal | Lista orizzontale |
numColumns | Griglia a N colonne |
2. TouchableOpacity — elementi cliccabili
Section titled “2. TouchableOpacity — elementi cliccabili”TouchableOpacity rende qualsiasi elemento cliccabile con feedback visivo (opacità quando premuto).
import { TouchableOpacity, Text } from 'react-native';
<TouchableOpacity style={styles.bottone} onPress={() => console.log('click!')} onLongPress={() => console.log('long press!')} activeOpacity={0.7} // ← quanto diventa trasparente (default: 0.2) disabled={false}> <Text style={styles.testo}>Cliccami</Text></TouchableOpacity>Pattern: lista cliccabile
Section titled “Pattern: lista cliccabile”const renderItem = ({ item }) => ( <TouchableOpacity style={styles.card} onPress={() => Alert.alert('Selezionato', item.nome)} onLongPress={() => {/* elimina */}} > <Text style={styles.nome}>{item.nome}</Text> </TouchableOpacity>);3. Esempio: Rubrica contatti completa
Section titled “3. Esempio: Rubrica contatti completa”import { View, Text, FlatList, TouchableOpacity, Alert, TextInput, StyleSheet } from 'react-native';import { useState } from 'react';
export default function App() { const [nome, setNome] = useState(''); const [tel, setTel] = useState(''); const [contatti, setContatti] = useState([ { id: '1', nome: 'Mario Rossi', tel: '345 1234567' }, { id: '2', nome: 'Sofia Bianchi', tel: '345 7654321' }, ]); const [prossimoId, setProssimoId] = useState(3);
const aggiungi = () => { if (!nome || !tel) { Alert.alert('Errore', 'Compila tutti i campi'); return; } setContatti([ ...contatti, { id: String(prossimoId), nome, tel } ]); setProssimoId(prossimoId + 1); setNome(''); setTel(''); };
const elimina = (id) => { setContatti(contatti.filter(c => c.id !== id)); };
const confermaElimina = (item) => { Alert.alert( 'Elimina contatto', `Eliminare ${item.nome}?`, [ { text: 'Annulla', style: 'cancel' }, { text: 'Elimina', onPress: () => elimina(item.id), style: 'destructive' }, ] ); };
const renderItem = ({ item }) => ( <TouchableOpacity style={styles.card} onPress={() => Alert.alert(item.nome, `Tel: ${item.tel}`)} onLongPress={() => confermaElimina(item)} > <View style={styles.avatar}> <Text style={styles.iniziale}>{item.nome[0]}</Text> </View> <View style={styles.info}> <Text style={styles.nome}>{item.nome}</Text> <Text style={styles.tel}>{item.tel}</Text> </View> <Text style={styles.chevron}>›</Text> </TouchableOpacity> );
return ( <View style={styles.container}> <Text style={styles.titolo}>📞 Rubrica</Text>
{/* Form aggiunta */} <View style={styles.form}> <TextInput style={styles.input} placeholder="Nome" value={nome} onChangeText={setNome} /> <TextInput style={styles.input} placeholder="Telefono" value={tel} onChangeText={setTel} keyboardType="phone-pad" /> <TouchableOpacity style={styles.aggiungiBtn} onPress={aggiungi}> <Text style={styles.aggiungiTesto}>+ Aggiungi</Text> </TouchableOpacity> </View>
{/* Lista */} <FlatList data={contatti} renderItem={renderItem} keyExtractor={item => item.id} ListEmptyComponent={ <Text style={styles.vuoto}>Nessun contatto</Text> } /> </View> );}
const styles = StyleSheet.create({ container: { flex: 1, paddingTop: 50, paddingHorizontal: 15, backgroundColor: '#f0f4f8', }, titolo: { fontSize: 28, fontWeight: 'bold', marginBottom: 15, }, form: { backgroundColor: 'white', padding: 15, borderRadius: 12, marginBottom: 15, elevation: 2, }, input: { borderWidth: 1, borderColor: '#ddd', padding: 10, borderRadius: 8, marginBottom: 8, fontSize: 16, }, aggiungiBtn: { backgroundColor: '#3498db', padding: 12, borderRadius: 8, alignItems: 'center', }, aggiungiTesto: { color: 'white', fontWeight: 'bold', fontSize: 16, }, card: { flexDirection: 'row', backgroundColor: 'white', padding: 15, borderRadius: 12, marginBottom: 8, alignItems: 'center', elevation: 1, }, avatar: { width: 45, height: 45, borderRadius: 22.5, backgroundColor: '#3498db', justifyContent: 'center', alignItems: 'center', marginRight: 12, }, iniziale: { color: 'white', fontSize: 18, fontWeight: 'bold', }, info: { flex: 1, }, nome: { fontSize: 16, fontWeight: 'bold', }, tel: { fontSize: 14, color: 'grey', }, chevron: { fontSize: 24, color: '#ccc', }, vuoto: { textAlign: 'center', color: 'grey', marginTop: 50, fontSize: 16, },});4. FlatList con sezioni (SectionList)
Section titled “4. FlatList con sezioni (SectionList)”Per dati raggruppati:
import { SectionList } from 'react-native';
const DATA = [ { title: 'Amici', data: ['Mario', 'Sofia'], }, { title: 'Famiglia', data: ['Anna', 'Luigi'], },];
<SectionList sections={DATA} renderItem={({ item }) => <Text>{item}</Text>} renderSectionHeader={({ section }) => ( <Text style={styles.sectionHeader}>{section.title}</Text> )} keyExtractor={(item, index) => index}/>5. ItemSeparatorComponent
Section titled “5. ItemSeparatorComponent”<FlatList data={DATA} renderItem={renderItem} ItemSeparatorComponent={() => ( <View style={{ height: 1, backgroundColor: '#eee', marginLeft: 55 }} /> )}/>6. Tabella riassuntiva
Section titled “6. Tabella riassuntiva”| Componente | Codice | Uso |
|---|---|---|
| FlatList | <FlatList data={d} renderItem={fn} /> | Liste virtualizzate |
| SectionList | <SectionList sections={s} ... /> | Liste con sezioni |
| TouchableOpacity | <TouchableOpacity onPress={fn}> | Elementi cliccabili |
| Alert.alert | Alert.alert('Titolo','Msg', btns) | Dialoghi nativi |
7. Esercizio autonomo
Section titled “7. Esercizio autonomo”🎯 Esercizio: “Lista della spesa”
Section titled “🎯 Esercizio: “Lista della spesa””Crea un’app che:
- Campo input per nome elemento + quantità
- Bottone “Aggiungi” → aggiunge alla FlatList
- TouchableOpacity su ogni elemento → segna come “comprato” (barrato)
- Long-press → elimina con conferma Alert
- ListEmptyComponent quando la lista è vuota
- Separatore tra gli elementi
Suggerimenti
Section titled “Suggerimenti”// Stato per listaconst [lista, setLista] = useState([]);
// Elemento tipo:{ id: 1, nome: 'Pane', qta: 2, comprato: false }
// Segna come comprato:const toggle = (id) => { setLista(lista.map(item => item.id === id ? { ...item, comprato: !item.comprato } : item ));};