├── .github
└── FUNDING.yml
├── .gitignore
├── .npmignore
├── README.md
├── android
├── .npmignore
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── rnimmersivebars
│ ├── ImmersiveBars.java
│ ├── ImmersiveBarsModule.java
│ └── ImmersiveBarsPackage.java
├── index.android.js
├── index.d.ts
├── index.js
├── package.json
├── promo
├── android_10.gif
└── android_m.gif
└── yarn.lock
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [crutchcorn] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | gradle/
3 | android/.gradle/
4 | gradlew
5 | gradlew.bat
6 | local.properties
7 | # Logs
8 | logs
9 | *.log
10 | npm-debug.log*
11 | *.iml
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 |
17 | # Directory for instrumented libs generated by jscoverage/JSCover
18 | lib-cov
19 |
20 | # Coverage directory used by tools like istanbul
21 | coverage
22 |
23 | # nyc test coverage
24 | .nyc_output
25 |
26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
27 | .grunt
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules
37 | jspm_packages
38 |
39 | # Optional npm cache directory
40 | .npm
41 |
42 | # Optional REPL history
43 | .node_repl_history
44 | .idea
45 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .idea
2 | Screenshots
3 | .gif
4 | *.iml
5 | # OSX
6 | #
7 | .DS_Store
8 | # Xcode
9 | #
10 | build/
11 | *.pbxuser
12 | !default.pbxuser
13 | *.mode1v3
14 | !default.mode1v3
15 | *.mode2v3
16 | !default.mode2v3
17 | *.perspectivev3
18 | !default.perspectivev3
19 | xcuserdata
20 | *.xccheckout
21 | *.moved-aside
22 | DerivedData
23 | *.hmap
24 | *.ipa
25 | *.xcuserstate
26 |
27 | # node.js
28 | #
29 | node_modules/
30 | npm-debug.log
31 | examples/
32 | screenshot/
33 | *.md
34 | promo/
35 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | React Native Immersive Bars
3 |
4 |
5 |
6 |
7 | [](https://www.npmjs.com/package/react-native-immersive-bars/)
8 |
9 |
10 |
11 |
12 |
13 |

14 |
15 |

16 |
17 |
18 |
19 | > Do you like the button slider that changes from light to dark mode? [That's another package that I've built: `react-native-button-toggle-group`](https://github.com/crutchcorn/react-native-button-toggle-group)
20 |
21 | [Google officially suggests apps that target Q or higher to render content under the statusbar and navbar](http://youtube.com/watch?v=Nf-fP2u9vjI).
22 | However, [doing so previously was difficult to do and required some customization and tweaking](https://unicorn-utterances.com/posts/draw-under-navbar-using-react-native/). Not to mention the variant support for other APIs.
23 |
24 | Well, this package enables you to easily do so across various Android APIs levels. It also mocks out usage with iOS and other platforms, so you're able to use it in cross-platform apps.
25 |
26 | > ⚠ This package only supports Android 5.0 and higher ⚠
27 | >
28 | > This package is meant to be used alongside [the `react-native-safe-area-context` package](https://github.com/th3rdwave/react-native-safe-area-context) in order
29 | > to provide proper padding and margins in order to keep scrollviews and others from being stuck forever under the navbar.
30 | >
31 | > Please refer to that package's README for more information on how to use it.
32 |
33 | ## Install
34 |
35 | ```
36 | npm i --save react-native-immersive-bars
37 | ```
38 |
39 | Or
40 |
41 | ```
42 | yarn add react-native-immersive-bars
43 | ```
44 |
45 | ### Android
46 |
47 | If you're targeting API 29+ in your React Native app, you need to do one more step to enable the fully transparent bars. Add:
48 |
49 | ```xml
50 | - false
51 | ```
52 |
53 | To:
54 |
55 | ```
56 | android\app\src\main\res\values\styles.xml
57 | ```
58 |
59 | ## Usage
60 |
61 | ### Usage in JavaScript
62 |
63 | ```jsx
64 | import {changeBarColors} from 'react-native-immersive-bars';
65 |
66 | // ...
67 |
68 | React.useEffect(() => {
69 | changeBarColors(isDarkMode, '#50000000', 'transparent');
70 | // or changeBarColors(isDarkMode);
71 | }, [isDarkMode]);
72 | ```
73 |
74 | The `changeBarColors` function has a single required parameter and two optional ones.
75 |
76 | - `isDarkMode` (Required): If the app is in dark mode or not - will apply proper styling to icons and statusbar/navbar background
77 | - `translucentLightStr` (Optional): When a translucent bar must be drawn (due to API restrictions), what color it should be drawn in light mode
78 | - `translucentDarkStr` (Optional): When a translucent bar must be drawn (due to API restrictions), what color it should be drawn in dark mode
79 |
80 | Both `translucentLightStr` and `translucentDarkStr` accept and color that [Color.parseColor](https://developer.android.com/reference/android/graphics/Color#parseColor(java.lang.String)) is able to handle as well as the string `'transparent'`.
81 |
82 | > THIS MEANS THAT THREE DIGIT HEX SHORTHAND LIKE `#FFF` WILL CAUSE YOUR APP TO CRASH
83 |
84 | ### Adding to `onCreate` (Optional)
85 |
86 | If you only use the JavaScript code, your app will flash the navbar once the App.tsx code finally renders.
87 | If you want to avoid a jump like that, you can edit your code in:
88 |
89 | ```
90 | android > app > src > main > java > yourpackagepath > MainActivity.java
91 | ```
92 |
93 | And change the code to reflect this:
94 |
95 | ```java
96 | import com.facebook.react.ReactActivity;
97 | import com.rnimmersivebars.ImmersiveBars; // here
98 |
99 | public class MainActivity extends ReactActivity {
100 | @Override
101 | protected void onCreate(Bundle savedInstanceState) {
102 | boolean isDarkMode = false; // Customize this to match your app's default theme
103 | ImmersiveBars.changeBarColors(this, isDarkMode); // here
104 | super.onCreate(savedInstanceState);
105 | }
106 | // ...other code
107 | }
108 | ```
109 |
110 | ## Alternatives
111 |
112 | If you don't need to use a fullscreen navbar, then you can simply change the color of the navbar itself with this package:
113 |
114 | - [`react-native-navigation-bar-color`](https://github.com/thebylito/react-native-navigation-bar-color)
115 |
116 | Note that this package does not play nice with [`react-native-safe-area-context`'s edge detection](https://github.com/th3rdwave/react-native-safe-area-context/).
117 |
118 |
119 | Otherwise, if you want to hide the navbar and the statusbar in their entirety, I'd suggest taking a look at the following package:
120 |
121 | - [`react-native-immersive`](https://github.com/mockingbot/react-native-immersive)
122 |
--------------------------------------------------------------------------------
/android/.npmignore:
--------------------------------------------------------------------------------
1 | /build
2 | gradle/
3 | gradlew
4 | gradlew.bat
5 | local.properties
6 | proguard-rules.pro
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | def getExtOrDefault(name, defaultValue) {
2 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : defaultValue
3 | }
4 |
5 | buildscript {
6 | repositories {
7 | jcenter()
8 | google()
9 | }
10 |
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:3.1.0'
13 | }
14 | }
15 |
16 | apply plugin: 'com.android.library'
17 |
18 | android {
19 | compileSdkVersion getExtOrDefault('compileSdkVersion', 29)
20 |
21 | defaultConfig {
22 | minSdkVersion getExtOrDefault('minSdkVersion', 21)
23 | targetSdkVersion getExtOrDefault('targetSdkVersion', 29)
24 | versionCode 1
25 | versionName "1.0"
26 | }
27 | lintOptions {
28 | abortOnError false
29 | }
30 | }
31 |
32 | repositories {
33 | mavenCentral()
34 | mavenLocal()
35 | google()
36 | }
37 |
38 | dependencies {
39 | api 'com.facebook.react:react-native:+'
40 | }
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/android/src/main/java/com/rnimmersivebars/ImmersiveBars.java:
--------------------------------------------------------------------------------
1 | package com.rnimmersivebars;
2 |
3 | import android.os.Build;
4 | import android.graphics.Color;
5 | import android.view.View;
6 | import android.view.Window;
7 | import android.app.Activity;
8 |
9 | public class ImmersiveBars {
10 | /**
11 | * Allowing usage of lambda for Promise replacement within the file
12 | */
13 | interface ChangeColorsCallback
14 | {
15 | void finished(boolean worked);
16 | }
17 |
18 | /**
19 | * For usage in the onCreate method
20 | */
21 | public static void changeBarColors(final Activity activity, final Boolean isDarkMode) {
22 | changeBarColors(activity, isDarkMode, "", "");
23 | }
24 |
25 | /**
26 | * For usage in the React Module
27 | */
28 | public static void changeBarColors(final Activity activity, final Boolean isDarkMode, final String translucentLightStr, final String translucentDarkStr) {
29 | if (activity == null) {
30 | return;
31 | }
32 | final Window window = activity.getWindow();
33 | activity.runOnUiThread(new Runnable() {
34 | @Override
35 | public void run() {
36 | /**
37 | * Handle the color setting
38 | */
39 | int translucentLightColor;
40 | if (translucentLightStr.isEmpty()) {
41 | translucentLightColor = Color.parseColor("#50000000");
42 | } else if (translucentLightStr.equals("transparent")) {
43 | translucentLightColor = Color.TRANSPARENT;
44 | } else {
45 | translucentLightColor = Color.parseColor(translucentLightStr);
46 | }
47 |
48 | int translucentDarkColor;
49 | if (translucentDarkStr.isEmpty() || translucentDarkStr.equals("transparent")) {
50 | translucentDarkColor = Color.TRANSPARENT;
51 | } else {
52 | translucentDarkColor = Color.parseColor(translucentDarkStr);
53 | }
54 |
55 | // Set the navbar to be drawn over
56 | // Both flags were added in Level 16
57 | int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
58 |
59 | // M was the first version that supported light mode status bar
60 | boolean shouldUseTransparentStatusBar = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
61 | // O was the first version that supported light mode nav bar
62 | boolean shouldUseTransparentNavBar = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
63 |
64 | if (shouldUseTransparentStatusBar) {
65 | window.setStatusBarColor(Color.TRANSPARENT);
66 | if (!isDarkMode) {
67 | flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
68 | }
69 | } else {
70 | window.setStatusBarColor(isDarkMode ? translucentDarkColor : translucentLightColor);
71 | }
72 |
73 | if (shouldUseTransparentNavBar) {
74 | window.setNavigationBarColor(Color.TRANSPARENT);
75 | if (!isDarkMode) {
76 | flags |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
77 | }
78 | } else {
79 | window.setNavigationBarColor(isDarkMode ? translucentDarkColor: translucentLightColor);
80 | }
81 |
82 | window.getDecorView().setSystemUiVisibility(flags);
83 | }
84 | });
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/android/src/main/java/com/rnimmersivebars/ImmersiveBarsModule.java:
--------------------------------------------------------------------------------
1 | package com.rnimmersivebars;
2 |
3 | import com.facebook.react.bridge.Arguments;
4 | import com.facebook.react.bridge.ReactApplicationContext;
5 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
6 | import com.facebook.react.bridge.ReactMethod;
7 | import com.facebook.react.bridge.Promise;
8 | import com.facebook.react.bridge.WritableMap;
9 | import com.facebook.react.uimanager.IllegalViewOperationException;
10 |
11 | public class ImmersiveBarsModule extends ReactContextBaseJavaModule {
12 | private static final String ERROR_NO_ACTIVITY = "E_NO_ACTIVITY";
13 | private static final String ERROR_NO_ACTIVITY_MESSAGE = "Tried to change the navigation bar while not attached to an Activity";
14 |
15 | public ImmersiveBarsModule(ReactApplicationContext reactContext) {
16 | super(reactContext);
17 | }
18 |
19 | @Override
20 | public String getName() {
21 | return "ImmersiveBars";
22 | }
23 |
24 | @ReactMethod
25 | public void changeBarColors(final Boolean isDarkMode, Promise promise) {
26 | ImmersiveBars.changeBarColors(
27 | getCurrentActivity(),
28 | isDarkMode,
29 | "",
30 | ""
31 | );
32 | }
33 |
34 | @ReactMethod
35 | public void changeBarColors(final Boolean isDarkMode, final String translucentLightStr, final String translucentDarkStr) {
36 | ImmersiveBars.changeBarColors(
37 | getCurrentActivity(),
38 | isDarkMode,
39 | translucentLightStr,
40 | translucentDarkStr
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/android/src/main/java/com/rnimmersivebars/ImmersiveBarsPackage.java:
--------------------------------------------------------------------------------
1 | package com.rnimmersivebars;
2 |
3 | import com.facebook.react.ReactPackage;
4 | import com.facebook.react.bridge.JavaScriptModule;
5 | import com.facebook.react.bridge.NativeModule;
6 | import com.facebook.react.bridge.ReactApplicationContext;
7 | import com.facebook.react.uimanager.ViewManager;
8 | import java.util.ArrayList;
9 | import java.util.Collections;
10 | import java.util.List;
11 |
12 | public class ImmersiveBarsPackage implements ReactPackage {
13 |
14 | // Deprecated RN 0.47
15 | public List> createJSModules() {
16 | return Collections.emptyList();
17 | }
18 |
19 | @Override
20 | public List createViewManagers(ReactApplicationContext reactContext) {
21 | return Collections.emptyList();
22 | }
23 |
24 | @Override
25 | public List createNativeModules(
26 | ReactApplicationContext reactContext) {
27 | List modules = new ArrayList<>();
28 | modules.add(new ImmersiveBarsModule(reactContext));
29 | return modules;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/index.android.js:
--------------------------------------------------------------------------------
1 | import { NativeModules } from "react-native";
2 |
3 | const { ImmersiveBars } = NativeModules;
4 |
5 | const changeBarColors = (
6 | isDarkMode = false,
7 | translucentLightStr = "",
8 | translucentDarkStr = "",
9 | ) => {
10 | ImmersiveBars.changeBarColors(isDarkMode, translucentLightStr, translucentDarkStr);
11 | };
12 |
13 | export { changeBarColors };
14 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module "react-native-immersive-bars" {
2 | export function changeBarColors(
3 | isDarkMode: boolean,
4 | translucentLightStr?: string,
5 | translucentDarkStr?: string
6 | ): void;
7 | }
8 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const changeBarColors = () => {};
2 |
3 | export { changeBarColors };
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-immersive-bars",
3 | "version": "1.0.4",
4 | "description": "React Native component to change bottom bar/navigation bar to be transparent on Android",
5 | "mainNote": "Do not add .js extension. It breaks the ability for metrto to detect platforms",
6 | "main": "index",
7 | "scripts": {
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/crutchcorn/react-native-immersive-bars.git"
13 | },
14 | "keywords": [
15 | "react-native",
16 | "react-native-component",
17 | "react-native-immersive-bars",
18 | "immersive-mode",
19 | "transparent-navbar",
20 | "transparent-statusbar",
21 | "android"
22 | ],
23 | "author": "crutchcorn",
24 | "license": "MIT",
25 | "bugs": {
26 | "url": "https://github.com/crutchcorn/react-native-immersive-bars/issues"
27 | },
28 | "peerDependencies": {
29 | "react-native": ">=0.57.0"
30 | },
31 | "homepage": "https://github.com/crutchcorn/react-native-immersive-bars#readme"
32 | }
33 |
--------------------------------------------------------------------------------
/promo/android_10.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oceanbit/react-native-immersive-bars/4358f5db97d68cee83b4f79d53d2da25d2b064b2/promo/android_10.gif
--------------------------------------------------------------------------------
/promo/android_m.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oceanbit/react-native-immersive-bars/4358f5db97d68cee83b4f79d53d2da25d2b064b2/promo/android_m.gif
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 |
--------------------------------------------------------------------------------