Files
FarmaFinder/.mimocode/plans/1783593202807-swift-garden.md
Ichitux 2f36ef685d
Run Tests on Branches / Detect Changes (push) Successful in 10s
Run Tests on Branches / Backend Tests (push) Successful in 2m12s
Run Tests on Branches / Frontend Tests (push) Has been skipped
Run Tests on Branches / Frontend Mobile Tests (push) Successful in 1m47s
Mobile App design
2026-07-09 13:33:54 +02:00

6.1 KiB

Plan: Dark Mode para FarmaFinder Mobile

Objetivo

Añadir soporte de modo oscuro a la app móvil con:

  • Detección automática del modo oscuro del sistema
  • Toggle manual en la pantalla de perfil (Configuración)
  • Persistencia de la preferencia del usuario

Arquitectura

1. Theme Store (Zustand + AsyncStorage)

Nuevo archivo: store/themeStore.ts

  • Estado: mode: 'light' | 'dark' | 'system'
  • Persistencia con @react-native-async-storage/async-storage
  • Acciones: setMode(), init() (carga del storage al arrancar)
  • Por defecto: 'system' (respeta el dispositivo)

2. Hook useThemeColor

Nuevo archivo: hooks/useThemeColor.ts

  • Usa useColorScheme() de React Native para detectar el color del sistema
  • Combina con el mode del themeStore para determinar el tema efectivo
  • Retorna el objeto colors apropiado (light o dark)
  • Los componentes consumirán este hook en vez de importar colors directamente

3. Paleta de colores oscura

Modificar: constants/theme.ts

Añadir exportación darkColors con la paleta oscura:

Token Light (actual) Dark (nuevo)
background #fbfbfb #121212
card #ffffff #1e1e1e
surfaceLow #f2f4f5 #2a2a2a
surface #eceeef #333333
surfaceHigh #e6e8e9 #3a3a3a
border #c0c9bb #3a3a3a
separator #c0c9bb #3a3a3a
text #111417 #f0f0f0
textSecondary #41493e #a0a0a0
textInverse #ffffff #111417
primaryContainer #cfead0 #1a3a1c
onPrimaryContainer #0d2b12 #cfead0
secondaryContainer #dbe7ff #1a2a4a
tertiaryContainer #efe7ff #2a1a4a
dangerContainer #feecec #3a1a1a
accentWarm #f5a97a #f5a97a (sin cambio)
primary, secondary, tertiary, scanButton, success, danger, warning (sin cambios) (sin cambios)

4. Provider wrapper

Nuevo archivo: components/ThemeProvider.tsx

  • Lee el mode del themeStore y el useColorScheme() del sistema
  • Calcula el tema efectivo (light o dark)
  • Provee colors y isDark vía React Context
  • Carga la preferencia guardada en AsyncStorage al montar

5. Integración en Root Layout

Modificar: app/_layout.tsx

  • Envolver toda la app con <ThemeProvider>
  • El StatusBar se ajusta: style={isDark ? 'light' : 'auto'}
  • Los colores del Stack header se leen del contexto

6. Toggle en el perfil

Modificar: app/(tabs)/profile.tsx

Añadir en la pantalla de perfil (antes de "Configuración") un nuevo item de menú:

  • Icono: moon-outline / sunny-outline según el tema actual
  • Texto: "Modo de pantalla"
  • Al pulsar, mostrar un picker/modal con 3 opciones:
    • Sistema (icono: phone-portrait) - respeta el SO
    • Claro (icono: sunny) - fuerza light
    • Oscuro (icono: moon) - fuerza dark
  • El toggle se muestra inline (sin modal extra) tipo Segmented Control

7. Actualizar todos los screens y components

Cada archivo que importa colors debe cambiar a usar el hook useThemeColor():

Archivo Cambio
app/_layout.tsx Usa ThemeProvider + context para header colors
app/(tabs)/_layout.tsx Tab bar y header leen colors del context
app/(tabs)/index.tsx const { colors } = useThemeColor()
app/(tabs)/search.tsx const { colors } = useThemeColor()
app/(tabs)/scan.tsx const { colors } = useThemeColor()
app/(tabs)/alerts.tsx const { colors } = useThemeColor()
app/(tabs)/profile.tsx const { colors, isDark } = useThemeColor()
app/(tabs)/map.tsx const { colors } = useThemeColor()
app/medicine/[id].tsx const { colors } = useThemeColor()
app/pharmacy/[id].tsx const { colors } = useThemeColor()
app/auth/login.tsx const { colors } = useThemeColor()
app/auth/register.tsx const { colors } = useThemeColor()
components/SearchBar.tsx const { colors } = useThemeColor()
components/MedicineCard.tsx const { colors } = useThemeColor()
components/LoadingSpinner.tsx const { colors } = useThemeColor()
components/StockBadge.tsx Añade colores dark para badges
components/BarcodeScanner.tsx const { colors } = useThemeColor()

Nota sobre StyleSheet: Los StyleSheet.create() se ejecutan una vez. Para que los estilos se actualicen con el tema, las propiedades que dependen de colors deben aplicarse vía style inline o moverse a funciones que retornen estilos dinámicos. La estrategia más limpia es:

  • Los colores de fondo/texto van como inline styles
  • Las estructuras (flex, padding, borderRadius) se quedan en StyleSheet.create

8. Colores hardcodeados

Los siguientes archivos tienen colores inline hardcodeados que necesitan variante dark:

  • StockBadge.tsx: #eaf7ec, #fff3cd, #feecec → añadir tokens al tema
  • search.tsx: suggestions con #ffffff, #f0f7ff, etc. → usar colores del tema
  • profile.tsx: feedback #eaf7ec, #cfead0, #fecaca → usar colores del tema
  • home/index.tsx: rgba(255,255,255,0.2/0.3) para iconos → ajustar opacidad

9. app.json

Modificar: app.json

Cambiar "userInterfaceStyle": "light""userInterfaceStyle": "automatic"

Orden de ejecución

  1. store/themeStore.ts (nuevo)
  2. hooks/useThemeColor.ts (nuevo)
  3. constants/theme.ts (añadir darkColors)
  4. components/ThemeProvider.tsx (nuevo)
  5. app/_layout.tsx (envolver con ThemeProvider)
  6. app/(tabs)/_layout.tsx (colores dinámicos)
  7. app/(tabs)/profile.tsx (toggle de tema)
  8. Todos los screens y components (migrar a hook)
  9. app.json (userInterfaceStyle: automatic)
  10. Corregir colores hardcodeados

Verificación

  1. Ejecutar npx expo start y verificar que la app arranca sin errores
  2. Probar en iOS/Android: el tema respeta la configuración del sistema
  3. Ir a Perfil > Modo de pantalla y cambiar entre Claro/Oscuro/Sistema
  4. Verificar que el cambio es inmediato sin recargar la app
  5. Cerrar y reabrir la app → la preferencia persiste
  6. Verificar que todos los screens se ven bien en ambos modos (sin texto invisible, sin fondos transparentes)
  7. Probar tablets ( responsive)