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