19-Tutorial React Native Paso a Paso - Gestor de Tareas (Sin Edici贸n)


馃殌 Paso 1: Configuraci贸n Inicial (5 minutos)

bash
# 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)

text
GestorTareasRN/
├── src/
│   ├── components/
│   │   ├── TareaList.js
│   │   ├── TareaForm.js
│   │   └── TareaItem.js
│   └── App.js
├── App.js
└── package.json

馃摝 Paso 3: App.js principal (3 minutos)

jsx
// 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)

jsx
// 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)

jsx
// 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)

jsx
// 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)

jsx
// 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)

jsx
// 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)

jsx
// 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)

jsx
// 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:

jsx
// 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:

  1. Pantalla inicial - Muestra formulario y lista

  2. Crear tarea - Llenar formulario y presionar "Crear Tarea"

  3. Ver tarea - Aparece en lista con botones de acci贸n

  4. Completar tarea - Presionar bot贸n "Completar"

  5. Eliminar tarea - Presionar bot贸n "Eliminar" con confirmaci贸n

Estructura visual en m贸vil:

text
馃摑 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:

text
┌─────────────────────────────┬─────────────────────────────┐
│                             │                             │
│  馃摑 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)

PasoTiempoDescripci贸n
15 minConfiguraci贸n inicial con Expo
22 minEstructura de carpetas
33 minApp.js principal
410 minTareaList.js inicial
515 minTareaForm.js
610 minIntegrar formulario
715 minTareaItem.js con botones
810 minIntegrar TareaItem
93 minAjustes finales App.js
102 minResponsive 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:

bash
# 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:

  1. Configurar proyecto React Native con Expo

  2. Componentes b谩sicos de React Native

  3. Manejo de estado con useState

  4. Props y componentes reutilizables

  5. Estilos con StyleSheet

  6. Eventos t谩ctiles (onPress)

  7. Formularios en React Native

  8. 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

Entradas m谩s populares de este blog

0-Sistema de Tareas

13-CRUD en Laravel para Web y API con Rutas Normales (No Resource)

10-Introducci贸n a Blade en Laravel con un Ejemplo Pr谩ctico