19-Tutorial React Native Paso a Paso - Gestor de Tareas (Sin Edici贸n)
馃殌 Paso 1: Configuraci贸n Inicial (5 minutos)
# 1.1 Crear proyecto React Native con Expo
npx create-expo-app GestorTareasRN
cd GestorTareasRN
# 1.2 Instalar dependencias b谩sicas
npm install
# 1.3 Iniciar el proyecto
npx expo start馃搧 Paso 2: Estructura de Carpetas (2 minutos)
GestorTareasRN/
├── src/
│ ├── components/
│ │ ├── TareaList.js
│ │ ├── TareaForm.js
│ │ └── TareaItem.js
│ └── App.js
├── App.js
└── package.json馃摝 Paso 3: App.js principal (3 minutos)
// App.js
import React from 'react';
import { SafeAreaView, StyleSheet } from 'react-native';
import TareaList from './src/components/TareaList';
export default function App() {
return (
<SafeAreaView style={styles.container}>
<TareaList />
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});✅ Verificaci贸n: App b谩sica funcionando
馃搵 Paso 4: TareaList.js inicial (10 minutos)
// src/components/TareaList.js
import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
ScrollView
} from 'react-native';
function TareaList() {
const [tareas, setTareas] = useState([
{ id: 1, titulo: 'Comprar leche', descripcion: 'Ir al supermercado', completada: false },
{ id: 2, titulo: 'Estudiar React Native', descripcion: 'Hacer ejercicios', completada: true },
]);
return (
<View style={styles.container}>
{/* Encabezado */}
<View style={styles.header}>
<Text style={styles.tituloPrincipal}>馃摑 Gestor de Tareas</Text>
</View>
{/* Contenido principal */}
<View style={styles.contenido}>
{/* Columna izquierda - vac铆a */}
<View style={styles.columna}>
<Text style={styles.subtitulo}>Formulario</Text>
<Text>Aqu铆 ir谩 el formulario</Text>
</View>
{/* Columna derecha - Lista */}
<View style={styles.columna}>
<Text style={styles.subtitulo}>Lista de Tareas</Text>
{tareas.map(tarea => (
<View key={tarea.id} style={styles.tarjetaTarea}>
<Text style={styles.tituloTarea}>{tarea.titulo}</Text>
<Text>{tarea.descripcion}</Text>
<Text>
Estado: {tarea.completada ? 'Completada' : 'Pendiente'}
</Text>
</View>
))}
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
header: {
alignItems: 'center',
marginBottom: 20,
},
tituloPrincipal: {
fontSize: 24,
fontWeight: 'bold',
},
contenido: {
flex: 1,
flexDirection: 'row',
},
columna: {
flex: 1,
padding: 10,
},
subtitulo: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 10,
},
tarjetaTarea: {
backgroundColor: '#f5f5f5',
padding: 15,
borderRadius: 8,
marginBottom: 10,
},
tituloTarea: {
fontSize: 16,
fontWeight: 'bold',
},
});
export default TareaList;✅ Verificaci贸n: Se ven las tareas en dos columnas
✏️ Paso 5: Crear TareaForm.js (15 minutos)
// src/components/TareaForm.js
import React, { useState } from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
Switch
} from 'react-native';
function TareaForm({ onSubmit }) {
const [formData, setFormData] = useState({
titulo: '',
descripcion: '',
completada: false,
});
const [error, setError] = useState('');
const handleChange = (campo, valor) => {
setFormData({
...formData,
[campo]: valor,
});
};
const handleSubmit = () => {
if (!formData.titulo.trim()) {
setError('El t铆tulo es obligatorio');
return;
}
onSubmit(formData);
setFormData({
titulo: '',
descripcion: '',
completada: false,
});
setError('');
};
return (
<View style={styles.formulario}>
<Text style={styles.tituloFormulario}>➕ Nueva Tarea</Text>
{error ? (
<View style={styles.errorContainer}>
<Text style={styles.errorText}>{error}</Text>
</View>
) : null}
{/* Campo T铆tulo */}
<View style={styles.campo}>
<Text style={styles.label}>T铆tulo:</Text>
<TextInput
style={styles.input}
value={formData.titulo}
onChangeText={(text) => handleChange('titulo', text)}
placeholder="Ej: Comprar leche"
/>
</View>
{/* Campo Descripci贸n */}
<View style={styles.campo}>
<Text style={styles.label}>Descripci贸n:</Text>
<TextInput
style={[styles.input, styles.textArea]}
value={formData.descripcion}
onChangeText={(text) => handleChange('descripcion', text)}
placeholder="Ej: Ir al supermercado"
multiline
numberOfLines={3}
/>
</View>
{/* Campo Completada */}
<View style={styles.switchContainer}>
<Text>Marcar como completada:</Text>
<Switch
value={formData.completada}
onValueChange={(value) => handleChange('completada', value)}
/>
</View>
{/* Bot贸n Crear */}
<TouchableOpacity style={styles.boton} onPress={handleSubmit}>
<Text style={styles.botonTexto}>➕ Crear Tarea</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
formulario: {
backgroundColor: '#f8f9fa',
padding: 20,
borderRadius: 10,
borderWidth: 1,
borderColor: '#dee2e6',
},
tituloFormulario: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 15,
textAlign: 'center',
},
errorContainer: {
backgroundColor: '#f8d7da',
padding: 10,
borderRadius: 5,
marginBottom: 15,
},
errorText: {
color: '#721c24',
textAlign: 'center',
},
campo: {
marginBottom: 15,
},
label: {
fontSize: 16,
marginBottom: 5,
fontWeight: '500',
},
input: {
borderWidth: 1,
borderColor: '#ced4da',
borderRadius: 5,
padding: 10,
fontSize: 16,
},
textArea: {
height: 80,
textAlignVertical: 'top',
},
switchContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 20,
},
boton: {
backgroundColor: '#4CAF50',
padding: 15,
borderRadius: 5,
alignItems: 'center',
},
botonTexto: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
});
export default TareaForm;馃攧 Paso 6: Actualizar TareaList.js para usar TareaForm (10 minutos)
// src/components/TareaList.js - Actualizado
import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
ScrollView
} from 'react-native';
import TareaForm from './TareaForm';
function TareaList() {
const [tareas, setTareas] = useState([
{ id: 1, titulo: 'Comprar leche', descripcion: 'Ir al supermercado', completada: false },
{ id: 2, titulo: 'Estudiar React Native', descripcion: 'Hacer ejercicios', completada: true },
]);
const crearTarea = (datos) => {
const nuevaTarea = {
...datos,
id: Date.now(), // ID 煤nico basado en timestamp
};
setTareas([...tareas, nuevaTarea]);
};
return (
<View style={styles.container}>
{/* Encabezado */}
<View style={styles.header}>
<Text style={styles.tituloPrincipal}>馃摑 Gestor de Tareas</Text>
</View>
{/* Contenido principal - ScrollView para desplazamiento */}
<ScrollView style={styles.scrollView}>
<View style={styles.contenido}>
{/* Columna izquierda - Formulario */}
<View style={styles.columna}>
<TareaForm onSubmit={crearTarea} />
</View>
{/* Columna derecha - Lista */}
<View style={styles.columna}>
<Text style={styles.subtitulo}>
Lista de Tareas ({tareas.length})
</Text>
{tareas.map(tarea => (
<View key={tarea.id} style={styles.tarjetaTarea}>
<Text style={styles.tituloTarea}>{tarea.titulo}</Text>
<Text style={styles.descripcionTarea}>{tarea.descripcion}</Text>
<Text style={[
styles.estadoTarea,
tarea.completada ? styles.completada : styles.pendiente
]}>
{tarea.completada ? '✅ Completada' : '⏳ Pendiente'}
</Text>
</View>
))}
</View>
</View>
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 15,
},
scrollView: {
flex: 1,
},
header: {
alignItems: 'center',
marginBottom: 20,
paddingTop: 10,
},
tituloPrincipal: {
fontSize: 24,
fontWeight: 'bold',
},
contenido: {
flexDirection: 'row',
},
columna: {
flex: 1,
padding: 10,
},
subtitulo: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 15,
},
tarjetaTarea: {
backgroundColor: '#f8f9fa',
padding: 15,
borderRadius: 8,
marginBottom: 10,
borderWidth: 1,
borderColor: '#e9ecef',
},
tituloTarea: {
fontSize: 16,
fontWeight: 'bold',
marginBottom: 5,
},
descripcionTarea: {
fontSize: 14,
color: '#6c757d',
marginBottom: 8,
},
estadoTarea: {
fontSize: 14,
fontWeight: 'bold',
},
completada: {
color: '#28a745',
},
pendiente: {
color: '#ffc107',
},
});
export default TareaList;✅ Verificaci贸n: ¡Ahora puedes crear tareas desde el formulario!
馃摝 Paso 7: Crear TareaItem.js (15 minutos)
// src/components/TareaItem.js
import React from 'react';
import {
View,
Text,
TouchableOpacity,
StyleSheet,
Alert
} from 'react-native';
function TareaItem({ tarea, onDelete, onToggle }) {
const confirmarEliminar = () => {
Alert.alert(
'Eliminar Tarea',
'¿Est谩s seguro de eliminar esta tarea?',
[
{ text: 'Cancelar', style: 'cancel' },
{ text: 'Eliminar', onPress: () => onDelete(tarea.id), style: 'destructive' },
]
);
};
return (
<View style={[
styles.tarjeta,
tarea.completada && styles.tarjetaCompletada
]}>
{/* Encabezado de la tarea */}
<View style={styles.encabezado}>
<Text style={[
styles.titulo,
tarea.completada && styles.tituloCompletado
]}>
{tarea.completada ? '✅ ' : '⏳ '}
{tarea.titulo}
</Text>
</View>
{/* Descripci贸n */}
<Text style={styles.descripcion}>
{tarea.descripcion}
</Text>
{/* Informaci贸n adicional */}
<View style={styles.infoContainer}>
<Text style={styles.info}>ID: {tarea.id}</Text>
<Text style={[
styles.estado,
tarea.completada ? styles.estadoCompletado : styles.estadoPendiente
]}>
{tarea.completada ? 'Completada' : 'Pendiente'}
</Text>
</View>
{/* Botones de acci贸n */}
<View style={styles.botonesContainer}>
<TouchableOpacity
style={[
styles.botonAccion,
tarea.completada ? styles.botonDesmarcar : styles.botonCompletar
]}
onPress={() => onToggle(tarea.id, !tarea.completada)}
>
<Text style={styles.botonTexto}>
{tarea.completada ? '↩️ Desmarcar' : '✅ Completar'}
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.botonAccion, styles.botonEliminar]}
onPress={confirmarEliminar}
>
<Text style={styles.botonTexto}>馃棏️ Eliminar</Text>
</TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
tarjeta: {
backgroundColor: '#ffffff',
padding: 15,
borderRadius: 10,
marginBottom: 10,
borderWidth: 1,
borderColor: '#dee2e6',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 2,
},
tarjetaCompletada: {
backgroundColor: '#e8f5e9',
borderColor: '#c8e6c9',
},
encabezado: {
marginBottom: 8,
},
titulo: {
fontSize: 17,
fontWeight: 'bold',
color: '#333',
},
tituloCompletado: {
color: '#2e7d32',
},
descripcion: {
fontSize: 15,
color: '#666',
marginBottom: 10,
lineHeight: 20,
},
infoContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 15,
},
info: {
fontSize: 12,
color: '#999',
},
estado: {
fontSize: 12,
fontWeight: 'bold',
paddingHorizontal: 8,
paddingVertical: 2,
borderRadius: 10,
},
estadoCompletado: {
backgroundColor: '#d4edda',
color: '#155724',
},
estadoPendiente: {
backgroundColor: '#fff3cd',
color: '#856404',
},
botonesContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
gap: 10,
},
botonAccion: {
flex: 1,
paddingVertical: 10,
borderRadius: 6,
alignItems: 'center',
},
botonCompletar: {
backgroundColor: '#4CAF50',
},
botonDesmarcar: {
backgroundColor: '#FF9800',
},
botonEliminar: {
backgroundColor: '#f44336',
},
botonTexto: {
color: 'white',
fontWeight: 'bold',
fontSize: 14,
},
});
export default TareaItem;馃攧 Paso 8: Actualizar TareaList.js para usar TareaItem (10 minutos)
// src/components/TareaList.js - Versi贸n final
import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
ScrollView,
TouchableOpacity
} from 'react-native';
import TareaForm from './TareaForm';
import TareaItem from './TareaItem';
function TareaList() {
const [tareas, setTareas] = useState([
{ id: 1, titulo: 'Comprar leche', descripcion: 'Ir al supermercado', completada: false },
{ id: 2, titulo: 'Estudiar React Native', descripcion: 'Hacer ejercicios', completada: true },
]);
const crearTarea = (datos) => {
const nuevaTarea = {
...datos,
id: Date.now(),
};
setTareas([...tareas, nuevaTarea]);
};
const eliminarTarea = (id) => {
setTareas(tareas.filter(t => t.id !== id));
};
const completarTarea = (id, completada) => {
setTareas(tareas.map(t =>
t.id === id ? { ...t, completada } : t
));
};
return (
<View style={styles.container}>
{/* Encabezado */}
<View style={styles.header}>
<Text style={styles.tituloPrincipal}>馃摑 Gestor de Tareas</Text>
<Text style={styles.subtituloPrincipal}>
Tareas totales: {tareas.length}
</Text>
</View>
{/* Contenido principal */}
<ScrollView
style={styles.scrollView}
contentContainerStyle={styles.scrollViewContent}
>
<View style={styles.contenido}>
{/* Columna izquierda - Formulario */}
<View style={styles.columnaIzquierda}>
<TareaForm onSubmit={crearTarea} />
{/* Bot贸n de recarga */}
<TouchableOpacity
style={styles.botonRecargar}
onPress={() => console.log('Recargar')}
>
<Text style={styles.botonRecargarTexto}>
馃攧 Recargar Tareas
</Text>
</TouchableOpacity>
</View>
{/* Columna derecha - Lista */}
<View style={styles.columnaDerecha}>
<View style={styles.headerLista}>
<Text style={styles.tituloLista}>
馃搵 Mis Tareas ({tareas.length})
</Text>
</View>
{tareas.length === 0 ? (
<View style={styles.vacioContainer}>
<Text style={styles.vacioTexto}>馃摥 No hay tareas</Text>
<Text style={styles.vacioSubtexto}>
¡Crea tu primera tarea usando el formulario!
</Text>
</View>
) : (
tareas.map((tarea) => (
<TareaItem
key={tarea.id}
tarea={tarea}
onDelete={eliminarTarea}
onToggle={completarTarea}
/>
))
)}
</View>
</View>
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f8f9fa',
},
header: {
backgroundColor: '#ffffff',
padding: 20,
alignItems: 'center',
borderBottomWidth: 1,
borderBottomColor: '#e9ecef',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.05,
shadowRadius: 3,
elevation: 3,
},
tituloPrincipal: {
fontSize: 26,
fontWeight: 'bold',
color: '#333',
},
subtituloPrincipal: {
fontSize: 14,
color: '#666',
marginTop: 5,
},
scrollView: {
flex: 1,
},
scrollViewContent: {
paddingBottom: 20,
},
contenido: {
flexDirection: 'row',
padding: 15,
},
columnaIzquierda: {
flex: 1,
paddingRight: 10,
},
columnaDerecha: {
flex: 1,
paddingLeft: 10,
},
headerLista: {
marginBottom: 15,
paddingBottom: 10,
borderBottomWidth: 1,
borderBottomColor: '#dee2e6',
},
tituloLista: {
fontSize: 20,
fontWeight: 'bold',
color: '#333',
},
botonRecargar: {
backgroundColor: '#6c757d',
padding: 12,
borderRadius: 6,
marginTop: 15,
alignItems: 'center',
},
botonRecargarTexto: {
color: 'white',
fontWeight: 'bold',
fontSize: 15,
},
vacioContainer: {
backgroundColor: '#ffffff',
padding: 30,
borderRadius: 10,
alignItems: 'center',
borderWidth: 1,
borderColor: '#e9ecef',
borderStyle: 'dashed',
},
vacioTexto: {
fontSize: 18,
fontWeight: 'bold',
color: '#6c757d',
marginBottom: 5,
},
vacioSubtexto: {
fontSize: 14,
color: '#adb5bd',
textAlign: 'center',
},
});
export default TareaList;馃幆 Paso 9: Ajustar App.js para mejor visualizaci贸n (5 minutos)
// App.js - Versi贸n final
import React from 'react';
import { SafeAreaView, StatusBar, StyleSheet } from 'react-native';
import TareaList from './src/components/TareaList';
export default function App() {
return (
<SafeAreaView style={styles.container}>
<StatusBar
barStyle="dark-content"
backgroundColor="#ffffff"
/>
<TareaList />
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f8f9fa',
},
});馃摫 Paso 10: Configurar pantallas responsive (5 minutos)
// src/utils/dimensions.js
import { Dimensions } from 'react-native';
const { width, height } = Dimensions.get('window');
export const isTablet = width >= 768;
export const isSmallDevice = width < 375;
export const screenWidth = width;
export const screenHeight = height;
export const responsiveFontSize = (size) => {
const scale = width / 375; // 375 es el ancho base (iPhone SE)
const newSize = size * scale;
return Math.round(newSize);
};Actualizar TareaList.js para responsive:
// Agregar al inicio de TareaList.js
import { useWindowDimensions } from 'react-native';
// Dentro del componente TareaList:
const { width } = useWindowDimensions();
const esTablet = width >= 768;
// Modificar estilo del contenido:
contenido: {
flexDirection: esTablet ? 'row' : 'column',
},
columnaIzquierda: {
flex: 1,
paddingRight: esTablet ? 10 : 0,
marginBottom: esTablet ? 0 : 20,
},
columnaDerecha: {
flex: 1,
paddingLeft: esTablet ? 10 : 0,
},✅ Verificaci贸n Final
Flujo de la aplicaci贸n:
Pantalla inicial - Muestra formulario y lista
Crear tarea - Llenar formulario y presionar "Crear Tarea"
Ver tarea - Aparece en lista con botones de acci贸n
Completar tarea - Presionar bot贸n "Completar"
Eliminar tarea - Presionar bot贸n "Eliminar" con confirmaci贸n
Estructura visual en m贸vil:
馃摑 Gestor de Tareas
Tareas totales: 2
[FORMULARIO CREAR TAREA]
➕ Nueva Tarea
T铆tulo: _______________
Descripci贸n: __________
Marcar como completada: [ ]
[➕ Crear Tarea]
[馃攧 Recargar Tareas]
[LISTA DE TAREAS (2)]
• ⏳ Comprar leche
Ir al supermercado
ID: 1 | Pendiente
[✅ Completar] [馃棏️ Eliminar]
• ✅ Estudiar React Native
Hacer ejercicios
ID: 2 | Completada
[↩️ Desmarcar] [馃棏️ Eliminar]Estructura visual en tablet:
┌─────────────────────────────┬─────────────────────────────┐
│ │ │
│ 馃摑 Gestor de Tareas │ 馃搵 Mis Tareas (2) │
│ Tareas totales: 2 │ │
│ │ • ⏳ Comprar leche │
│ ➕ Nueva Tarea │ Ir al supermercado │
│ T铆tulo: _______________ │ ID: 1 | Pendiente │
│ │ [✅ Completar][馃棏️ Elim] │
│ Descripci贸n: _________ │ │
│ ______ │ • ✅ Estudiar React Native │
│ │ Hacer ejercicios │
│ Marcar como completada: [ ]│ ID: 2 | Completada │
│ │ [↩️ Desmarcar][馃棏️ Elim] │
│ [➕ Crear Tarea] │ │
│ │ │
│ [馃攧 Recargar Tareas] │ │
│ │ │
└─────────────────────────────┴─────────────────────────────┘馃搳 Resumen de Tiempo (75 minutos total)
| Paso | Tiempo | Descripci贸n |
|---|---|---|
| 1 | 5 min | Configuraci贸n inicial con Expo |
| 2 | 2 min | Estructura de carpetas |
| 3 | 3 min | App.js principal |
| 4 | 10 min | TareaList.js inicial |
| 5 | 15 min | TareaForm.js |
| 6 | 10 min | Integrar formulario |
| 7 | 15 min | TareaItem.js con botones |
| 8 | 10 min | Integrar TareaItem |
| 9 | 3 min | Ajustes finales App.js |
| 10 | 2 min | Responsive b谩sico |
✅ Caracter铆sticas implementadas:
1. CRUD b谩sico:
✅ Crear nuevas tareas
✅ Listar todas las tareas
✅ Marcar como completadas
✅ Eliminar tareas (con confirmaci贸n)
2. Interfaz React Native:
✅ Componentes nativos (View, Text, TextInput, etc.)
✅ TouchableOpacity para botones
✅ Switch para checkbox
✅ Alert para confirmaciones
✅ ScrollView para desplazamiento
3. Estilos m铆nimos:
✅ StyleSheet para estilos
✅ Colores b谩sicos de material
✅ Dise帽o responsive b谩sico
✅ Sin librer铆as externas
4. UX mejorada:
✅ Confirmaci贸n al eliminar
✅ Feedback visual de estados
✅ Formulario con validaci贸n
✅ Dise帽o claro y simple
馃摫 Para ejecutar:
# Desarrollo
npx expo start
# iOS (si tienes Mac)
npx expo start --ios
# Android (si tienes emulador/configurado)
npx expo start --android
# Web (tambi茅n funciona en navegador)
npx expo start --web馃幆 Lo que aprendi贸 el estudiante:
Configurar proyecto React Native con Expo
Componentes b谩sicos de React Native
Manejo de estado con useState
Props y componentes reutilizables
Estilos con StyleSheet
Eventos t谩ctiles (onPress)
Formularios en React Native
Alertas y confirmaciones
¡Perfecto! El alumno ahora tiene una aplicaci贸n m贸vil funcional de gestor de tareas en React Native, con CRUD b谩sico y dise帽o responsivo, siguiendo la misma estructura simple del tutorial web.
Comentarios
Publicar un comentario