BhavanPatel / react-native-navbar-color

Module for change color of react-native footer navigation bar and Statusbar
MIT License
53 stars 19 forks source link

This is useless if you can't change the colors of the nav bar icons along with the nav bar. #8

Open gjstreicher opened 3 years ago

FE-Sobrero commented 3 years ago

I had a similar issue.

When setting the navbar live between light and dark themes, the icons would stick to light colors, making them indistinguishable when against a light background.

My (Local) Fix/Mod to the package

My project is only using API 30+ for changing navbar color, so the following changes to my local build worked for me:

Options to fix

I chose to fix with the easy solution ( b ).

Mods to the node module, react-native-navbar-color

Note, my changes are only for API 30+. This is pretty restrictive. You can add support for lower api's if you want. This was a quick mod on my end to work with only new(est) android versions, and ignore setting on older.

RNNavBarColorModule.java

package com.bhavan.RNNavBarColor;

import android.app.Activity;
import android.view.Window;
import android.view.View;
import android.view.WindowInsetsController;
import android.graphics.Color;
import android.os.Build;

import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactMethod;

import java.util.Map;
import java.util.HashMap;

public class RNNavBarColorModule extends ReactContextBaseJavaModule {
    public RNNavBarColorModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    @Override
    public String getName() {
        return "RNNavBarColor";
    }

    @ReactMethod
    public void setColor(final String color, final boolean dark) {
        final Activity activity = getCurrentActivity();
        final int colorInt = Color.parseColor(color);
        if(activity == null)
            return;
        final Window window = activity.getWindow();
        final WindowInsetsController insetsController = window.getDecorView().getWindowInsetsController();

        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                window.setNavigationBarColor(colorInt);
                if (dark) {
                    insetsController.setSystemBarsAppearance(0, WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS);
                } else {
                    insetsController.setSystemBarsAppearance(WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS, WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS);

                }
            }
        });
    }

    @Override
    public Map<String, Object> getConstants() {
        HashMap<String, Object> constants = new HashMap<String, Object>();
        constants.put("apiLevel", Build.VERSION.SDK_INT);
        return constants;
    }

}

build.gradle

apply plugin: 'com.android.library'

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.0"

    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 30
        compileSdkVersion 30
        versionCode 1
        versionName "1.0"
    }
}

dependencies {
    compile "com.facebook.react:react-native:+" 
}

index.js

import { Platform, StatusBar, NativeModules } from "react-native";

let NavigationBar = NativeModules.RNNavBarColor;

module.exports = {
    getAPILevel: () => {
        if (Platform.OS == "android") {
            return NavigationBar.apiLevel;
        }
    },
    setColor: (color, dark) => {
        if (Platform.OS == "android" && NavigationBar.apiLevel >= 30) {
            return NavigationBar.setColor(color, dark);
        }
    },
    setStatusBarColor: (color, animation) => {
        if (Platform.OS == "android") {
            return StatusBar.setBackgroundColor(color, animation);
        }
    },
    setStatusBarTheme: (theme, animation) => {
        if (theme == "light") {
            return StatusBar.setBarStyle("light-content", animation);
        } else if (theme == "dark") {
            return StatusBar.setBarStyle("dark-content", animation);
        } else {
            return StatusBar.setBarStyle("default", animation);
        }
    },
};
Local Project Mods (In your project)

I added this style to my appbar. Unsure if necessary.

<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">

For my app, the user can change themes at runtime, or use device default. So, I added a side-effect to update the NavigationBar to match the theme's background & contrast mode.

//ie, if theme = { colors:{background:'#ffffff'}, dark:false} 
    React.useLayoutEffect(() => {
        NavigationBar.setColor(theme.colors.background, theme.dark);
    }, [theme]);