Sep 10, 2023
Transparent Android Status Bar in Expo
So you've got your app to be edge-to-edge but still have that ugly status bar background scrim that refuses to go away? Here's the actual solution.
If you've looked this issue up in the past then you've likely stumbled upon answers such as setting FLAG_LAYOUT_NO_LIMITS, but this breaks Samsung One UI's virtual navbars as they have no block height for safe area view's to reference.
// In Activity's onCreate() for instance
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Window w = getWindow();
w.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
}
- Tell your app to draw behind the system bars within the Activity
onCreate
:WindowCompat.setDecorFitsSystemWindows(getWindow(), false)
- Set the system bar colours to be transparent
<item name=”android:statusBarColor”>@android:color/transparent</item>
- Set
androidStatusBar.translucent
tofalse
inapp.config.js/app.config.js
Translucent does not include transparent in Android
Obviously this isn't very Expo friendly, we're editing native Android files here, so let's turn it into a config plugin.
import { ConfigPlugin, withMainActivity, withAndroidStyles, AndroidConfig } from "@expo/config-plugins"
import { addImports } from "@expo/config-plugins/build/android/codeMod"
import { mergeContents } from "@expo/config-plugins/build/utils/generateCode"
import { ExpoConfig } from "@expo/config-types"
const TRANSPARENT_WINDOW_ID = "transparent-window-mainActivity-onCreate"
export const withAndroidTransparentWindow: ConfigPlugin = (config) => {
config = withAndroidStyles(config, (config) => {
config.modResults = AndroidConfig.Styles.assignStylesValue(config.modResults, {
add: true,
parent: AndroidConfig.Styles.getAppThemeLightNoActionBarGroup(),
name: "android:statusBarColor",
value: "@android:color/transparent",
})
return config
})
return withMainActivity(config, (config) => {
const isJava = config.modResults.language === "java"
const LE = isJava ? ";" : ""
config.modResults.contents = addImports(config.modResults.contents, ["androidx.core.view.WindowCompat", "android.os.Build"], isJava)
config.modResults.contents = mergeContents({
src: config.modResults.contents,
anchor: /(?<=^.*super\.onCreate.*$)/m,
offset: 1,
comment: "//",
tag: TRANSPARENT_WINDOW_ID,
newSrc: `
if (Build.VERSION.SDK_INT > 28) {
WindowCompat.setDecorFitsSystemWindows(getWindow(), false)${LE}
}
`,
}).contents
return config
})
}
export default withAndroidTransparentWindow
This will modify our android folder when running expo prebuild
so that we're not in a bare workflow; this config plugin has been written in TypeScript and will therefore need compiling before referencing in the app config.