RN Extra — Template Starter
Progetto Expo completo con navigazione, persistenza e componenti base.
Setup rapido
Section titled “Setup rapido”npx create-expo-app@latest MioProgetto --template blankcd MioProgettonpx expo install @react-native-async-storage/async-storage expo-routernpx expo install @expo/vector-iconsStruttura
Section titled “Struttura”MioProgetto/├── app/│ ├── _layout.js│ ├── index.js│ └── (tabs)/│ ├── _layout.js│ ├── index.js│ └── profilo.js├── components/│ ├── Card.js│ └── Loading.js├── utils/│ └── storage.js└── app.jsonFile completi
Section titled “File completi”app/_layout.js
Section titled “app/_layout.js”import { Stack } from 'expo-router';
export default function RootLayout() { return ( <Stack> <Stack.Screen name="index" options={{ headerShown: false }} /> <Stack.Screen name="(tabs)" options={{ headerShown: false }} /> </Stack> );}app/index.js (splash / login)
Section titled “app/index.js (splash / login)”import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';import { router } from 'expo-router';
export default function Welcome() { return ( <View style={styles.container}> <Text style={styles.emoji}>📱</Text> <Text style={styles.titolo}>Mia App</Text> <Text style={styles.sottotitolo}>La mia prima app React Native</Text> <TouchableOpacity style={styles.bottone} onPress={() => router.replace('/(tabs)')} > <Text style={styles.bottoneTesto}>Inizia</Text> </TouchableOpacity> </View> );}
const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#3498db', padding: 30, }, emoji: { fontSize: 60, marginBottom: 20 }, titolo: { fontSize: 32, fontWeight: 'bold', color: 'white', marginBottom: 10 }, sottotitolo: { fontSize: 16, color: 'rgba(255,255,255,0.8)', marginBottom: 40 }, bottone: { backgroundColor: 'white', paddingHorizontal: 40, paddingVertical: 15, borderRadius: 25, }, bottoneTesto: { color: '#3498db', fontWeight: 'bold', fontSize: 18 },});components/Card.js
Section titled “components/Card.js”import { View, Text, StyleSheet } from 'react-native';
export default function Card({ children, style }) { return ( <View style={[styles.card, style]}> {children} </View> );}
const styles = StyleSheet.create({ card: { backgroundColor: 'white', padding: 15, borderRadius: 12, marginBottom: 10, elevation: 2, shadowColor: '#000', shadowOpacity: 0.08, shadowRadius: 8, shadowOffset: { width: 0, height: 2 }, },});components/Loading.js
Section titled “components/Loading.js”import { View, ActivityIndicator, Text, StyleSheet } from 'react-native';
export default function Loading({ message = 'Caricamento...' }) { return ( <View style={styles.container}> <ActivityIndicator size="large" color="#3498db" /> <Text style={styles.text}>{message}</Text> </View> );}
const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#f5f5f5', }, text: { marginTop: 10, color: 'grey', fontSize: 16, },});utils/storage.js
Section titled “utils/storage.js”import AsyncStorage from '@react-native-async-storage/async-storage';
export const saveData = async (key, data) => { try { await AsyncStorage.setItem(key, JSON.stringify(data)); return true; } catch (e) { console.error('Save error:', e); return false; }};
export const loadData = async (key) => { try { const json = await AsyncStorage.getItem(key); return json ? JSON.parse(json) : []; } catch (e) { console.error('Load error:', e); return []; }};
export const removeData = async (key) => { try { await AsyncStorage.removeItem(key); return true; } catch (e) { console.error('Remove error:', e); return false; }};utils/api.js
Section titled “utils/api.js”const BASE_URL = 'https://jsonplaceholder.typicode.com';
export const fetchData = async (endpoint) => { const res = await fetch(`${BASE_URL}/${endpoint}`); if (!res.ok) throw new Error(`HTTP ${res.status}`); return res.json();};
export const postData = async (endpoint, data) => { const res = await fetch(`${BASE_URL}/${endpoint}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }); if (!res.ok) throw new Error(`HTTP ${res.status}`); return res.json();};