Error al cambiar el color blanco en la barra de navegación de Android en React Native
Hace unas semanas, despues de algunas actualizaciones en RN (v0.79.7), me encontré con un comportamiento inesperado en Deeditt y la barra de navegación. Aunque había definido correctamente el color personalizado para la barra (la parte inferior donde están los botones del sistema en Android), en una de las vistas esa barra se volvía blanca sin razón aparente.
Lo más curioso era que el problema solo ocurría en una pantalla específica, una en la que también estaba usando KeyboardProvider (de la librería react-native-keyboard-controller) y navegación por pestañas con @react-navigation/bottom-tabs.
Después de investigar bastante, encontré que este es un fallo común en Android con React Native y que requiere una solución dividida entre código nativo y React.
Los Síntomas
- La app inicia mostrando correctamente el color personalizado
- Al navegar a una vista específica, la barra se pone blanca
- Incluso al volver atrás, el color sigue en blanco
- El color blanco persiste entre sesiones
- Solo afecta a Android
El Diagnóstico
El problema aunque no estoy totalmente seguro, creo que se debe a una combinación de tres factores:
1. Diferencia entre configuración estática y dinámica
Normalmente, el color de la barra se define en styles.xml así:
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:navigationBarColor">@color/purple</item>
<item name="android:windowLightNavigationBar">false</item>
</style>
Esta definición se aplica solo en el arranque de la app. Luego, otros eventos (como cambios de pantalla, apertura del teclado o librerías externas) pueden sobrescribir ese valor.
2. KeyboardProvider toma el control de la barra
La librería react-native-keyboard-controller puede tomar el control tanto del statusBar (arriba) como del navigationBar (abajo) cuando se usa con la prop statusBarTranslucent. Y si no especificamos lo contrario, puede restablecer la barra inferior al color blanco por defecto.
Ejemplo que causa el problema:
<KeyboardProvider statusBarTranslucent>
<KeyboardAvoidingView>
{/* contenido */}
</KeyboardAvoidingView>
</KeyboardProvider>
3. Ciclo de vida de Android limitado para React Navigation
Los métodos típicos de actividad en Android (onCreate, onStart, onResume) no se ejecutan cuando haces una navegación de pantalla o tab dentro de React Native. Por lo tanto, no puedes confiar solo en esos métodos para mantener el color de la barra de navegación.
Como lo Solucioné
Hay que hacer dos cosas:
-
En código nativo, volver a aplicar el color manualmente cuando sea necesario
-
En código React Native, evitar que
KeyboardProvidermodifique la barra inferior
Parte 1 – Código nativo: MainActivity.kt
Aquí dejo el código con comentarios detallados para entender qué hace cada sección:
class MainActivity : ReactActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// Aplica el tema personalizado antes de mostrar la vista principal
setTheme(R.style.AppTheme)
// Establece una pantalla de arranque (opcional)
setContentView(R.layout.launch_screen)
super.onCreate(savedInstanceState)
// Inicializa el splash screen personalizado
RNBootSplash.init(this, R.style.BootTheme)
// Establece manualmente el color de la barra de navegación
setNavigationBarColor()
}
override fun onStart() {
super.onStart()
// Al volver a primer plano, reestablece el color
setNavigationBarColor()
}
override fun onResume() {
super.onResume()
// Por seguridad, vuelve a aplicar el color al reanudar
setNavigationBarColor()
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
// Este evento se lanza al cambiar de pantalla dentro de la app
if (hasFocus) {
setNavigationBarColor()
}
}
/**
* Aplica el color (púrpura en mi caso) a la barra inferior de navegación
*/
private fun setNavigationBarColor() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// Cambia el color de fondo de la barra
window.navigationBarColor = ContextCompat.getColor(this, R.color.purple)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Asegura que los iconos sean claros (blancos) en fondo oscuro
var flags = window.decorView.systemUiVisibility
flags = flags and android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv()
window.decorView.systemUiVisibility = flags
}
}
}
}
Parte 2 – React Native: corrije el uso de KeyboardProvider
Si en tus pantallas usas KeyboardProvider, asegúrate de agregar navigationBarTranslucent junto a statusBarTranslucent, esto es algo que encontré que comunmente se menciona en los comentarios de otros devs, pero definitivamente para mi no fue suficiente:
Código que causa el error
<KeyboardProvider statusBarTranslucent>
Código corregido
<KeyboardProvider statusBarTranslucent navigationBarTranslucent>
¿Por qué esto lo soluciona?
Al incluir navigationBarTranslucent, le estás diciendo a KeyboardProvider que no controle la barra inferior, y así deja intacto el color establecido desde el código nativo.
Verificaciones adicionales
Asegúrate de definir los colores
Desde el projecto de Android, en la carpta de resources/values, ubica el archivo colors.xml:
<resources>
<color name="purple">#6C4DDA</color>
</resources>
y en la misma carpeta esta el archivo styles.xml:
<resources>
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:statusBarColor">@color/purple</item>
<item name="android:navigationBarColor">@color/purple</item>
<item name="android:windowLightNavigationBar">false</item>
</style>
</resources>
Cómo probar si todo funciona
Después de los cambios, haz una build limpia:
cd android && ./gradlew clean && cd ..
npx react-native run-android
Y prueba:
- Lanzar la app → barra púrpura
- Navegar entre pantallas → color se mantiene
- Abrir teclado en formularios → color no cambia
- Cambiar de pestañas → sin flashes blancos
- Minimizar y restaurar la app → color persiste
Algunas Consideraciones adicionales
Compatibilidad con modo oscuro
Puedes adaptar el método setNavigationBarColor() para que use un color distinto en modo oscuro:
val isDarkMode = resources.configuration.uiMode and
Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
val colorRes = if (isDarkMode) R.color.dark_purple else R.color.purple
window.navigationBarColor = ContextCompat.getColor(this, colorRes)
Problemas en dispositivos Samsung
Algunos dispositivos Samsung ignoran los colores por defecto. Agrega esto a android/gradle.properties:
android.enforceNavigationBarContrast=false
Conclusión
Este es uno de esos bugs difíciles de identificar porque parece aleatorio, pero tiene una causa específica, esta vez me ha tomado un par de días identiicarlo, pero finalmente encontré la solución.
En mi caso, el problema solo se presentaba en una pantalla específica, y eso me ayudó a identificar la razón, aunque inicialmente no fuese tan obio que la razón era que usaba KeyboardProvider.
Al final solo bastó con agregar la prop navigationBarTranslucent y reforzar el color desde el código nativo para solucionarlo.
Happy coding! :)
Photo by Srinivasan on Unsplash
Written with StackEdit.