
Control system-bar icon appearance from Compose: toggle light/dark icons, make navigation bar transparent, disable contrast enforcement, and automatically restore prior settings when requests leave composition.
Set status bar and navigation bar icon colors from Compose Multiplatform. Each screen declares the icons it needs. The library applies them on Android and iOS, and brings back the previous ones when the screen leaves.
Add the library to commonMain. It is published to Maven Central:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("com.composables:systembars:1.0.0")
}
}
}Make the status and navigation bar icons dark by passing SystemBarContent.Dark to the SystemBars() composable:
import com.composables.systembars.SystemBarContent
import com.composables.systembars.SystemBars
@Composable
fun HomeScreen() {
SystemBars(SystemBarContent.Dark)
// Your UI
}SystemBarContent sets the color of everything drawn on a bar: its icons, text and handle. Dark is for light backgrounds behind the bar, Light is for dark ones.
iOS does not let an app set the status bar icon color directly, the way Android does. Instead, UIKit asks the UIViewController on screen for its preferredStatusBarStyle. The library needs a reference to that view controller so it can update the icons. Without one, SystemBars has no effect on iOS.
There are two ways to provide it:
If your app does not need its own view controller, host your Compose content in SystemBarsViewController instead of ComposeUIViewController:
import com.composables.systembars.SystemBarsViewController
fun MainViewController() = SystemBarsViewController {
App()
}If you already have a view controller, or another library provides one, create a SystemBarsState for it, return its statusBarStyle from preferredStatusBarStyle(), and provide the state to your Compose content. The state tells the controller to update whenever the requested icons change:
@OptIn(ExperimentalForeignApi::class)
class AppViewController : UIViewController(null, null) {
private val systemBarsState = SystemBarsState(this)
override fun preferredStatusBarStyle() = systemBarsState.statusBarStyle
override fun viewDidLoad() {
super.viewDidLoad()
val compose = ComposeUIViewController {
ProvideSystemBars(systemBarsState) { App() }
}
addChildViewController(compose)
view.addSubview(compose.view)
compose.view.setFrame(view.bounds)
compose.view.autoresizingMask = UIViewAutoresizingFlexibleWidth or UIViewAutoresizingFlexibleHeight
compose.didMoveToParentViewController(this)
}
}Make the status bar icons white, for example over a dark header image, by passing SystemBarContent.Light as statusBar. The navigation bar stays as it is:
SystemBars(statusBar = SystemBarContent.Light)Make the navigation bar icons dark, for example over a light bottom bar, by passing SystemBarContent.Dark as navigationBar. The status bar stays as it is:
SystemBars(navigationBar = SystemBarContent.Dark)This works on Android 8.0 (API 26) and later. Earlier versions always draw white navigation buttons, so the library leaves their navigation bar untouched. On iOS, the home indicator adapts its color to the content behind it automatically.
Make the status bar icons white and the navigation bar icons dark by passing both to SystemBars():
SystemBars(statusBar = SystemBarContent.Light, navigationBar = SystemBarContent.Dark)Make the icons white only while a photo viewer is open, and dark again when it closes, by calling SystemBars() inside the viewer:
@Composable
fun Gallery() {
SystemBars(SystemBarContent.Dark)
var viewing by remember { mutableStateOf<Photo?>(null) }
PhotoGrid(onClick = { viewing = it })
viewing?.let { photo ->
SystemBars(SystemBarContent.Light)
PhotoViewer(photo, onClose = { viewing = null })
}
}Make only the status bar icons white while a dark banner is showing, and keep the screen's dark navigation bar icons, by leaving navigationBar unspecified in the banner. Each bar comes from the most recent request that specifies it:
SystemBars(SystemBarContent.Dark) // screen: both bars dark
if (showBanner) {
SystemBars(statusBar = SystemBarContent.Light) // overlay: status bar light, navigation bar stays dark
DarkBanner()
}Draw mock system bars in previews, design tools or tests by wrapping your content in ProvideSystemBars() and reading the requested icons from its SystemBarsState. Nothing is applied to the platform inside it:
val systemBarsState = remember { SystemBarsState() }
ProvideSystemBars(systemBarsState) {
App()
}
FakeStatusBar(icons = systemBarsState.appearance.statusBar)SystemBars needs an Activity or dialog window. Elsewhere, such as in a service overlay, it does nothing and logs a warning once. Previews skip the warning.Light on dark backgrounds, Dark on light ones.SystemBarsViewController, or create a SystemBarsState(viewController) and return its statusBarStyle from that controller. Without a host, SystemBars does nothing and logs a warning once.Set status bar and navigation bar icon colors from Compose Multiplatform. Each screen declares the icons it needs. The library applies them on Android and iOS, and brings back the previous ones when the screen leaves.
Add the library to commonMain. It is published to Maven Central:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("com.composables:systembars:1.0.0")
}
}
}Make the status and navigation bar icons dark by passing SystemBarContent.Dark to the SystemBars() composable:
import com.composables.systembars.SystemBarContent
import com.composables.systembars.SystemBars
@Composable
fun HomeScreen() {
SystemBars(SystemBarContent.Dark)
// Your UI
}SystemBarContent sets the color of everything drawn on a bar: its icons, text and handle. Dark is for light backgrounds behind the bar, Light is for dark ones.
iOS does not let an app set the status bar icon color directly, the way Android does. Instead, UIKit asks the UIViewController on screen for its preferredStatusBarStyle. The library needs a reference to that view controller so it can update the icons. Without one, SystemBars has no effect on iOS.
There are two ways to provide it:
If your app does not need its own view controller, host your Compose content in SystemBarsViewController instead of ComposeUIViewController:
import com.composables.systembars.SystemBarsViewController
fun MainViewController() = SystemBarsViewController {
App()
}If you already have a view controller, or another library provides one, create a SystemBarsState for it, return its statusBarStyle from preferredStatusBarStyle(), and provide the state to your Compose content. The state tells the controller to update whenever the requested icons change:
@OptIn(ExperimentalForeignApi::class)
class AppViewController : UIViewController(null, null) {
private val systemBarsState = SystemBarsState(this)
override fun preferredStatusBarStyle() = systemBarsState.statusBarStyle
override fun viewDidLoad() {
super.viewDidLoad()
val compose = ComposeUIViewController {
ProvideSystemBars(systemBarsState) { App() }
}
addChildViewController(compose)
view.addSubview(compose.view)
compose.view.setFrame(view.bounds)
compose.view.autoresizingMask = UIViewAutoresizingFlexibleWidth or UIViewAutoresizingFlexibleHeight
compose.didMoveToParentViewController(this)
}
}Make the status bar icons white, for example over a dark header image, by passing SystemBarContent.Light as statusBar. The navigation bar stays as it is:
SystemBars(statusBar = SystemBarContent.Light)Make the navigation bar icons dark, for example over a light bottom bar, by passing SystemBarContent.Dark as navigationBar. The status bar stays as it is:
SystemBars(navigationBar = SystemBarContent.Dark)This works on Android 8.0 (API 26) and later. Earlier versions always draw white navigation buttons, so the library leaves their navigation bar untouched. On iOS, the home indicator adapts its color to the content behind it automatically.
Make the status bar icons white and the navigation bar icons dark by passing both to SystemBars():
SystemBars(statusBar = SystemBarContent.Light, navigationBar = SystemBarContent.Dark)Make the icons white only while a photo viewer is open, and dark again when it closes, by calling SystemBars() inside the viewer:
@Composable
fun Gallery() {
SystemBars(SystemBarContent.Dark)
var viewing by remember { mutableStateOf<Photo?>(null) }
PhotoGrid(onClick = { viewing = it })
viewing?.let { photo ->
SystemBars(SystemBarContent.Light)
PhotoViewer(photo, onClose = { viewing = null })
}
}Make only the status bar icons white while a dark banner is showing, and keep the screen's dark navigation bar icons, by leaving navigationBar unspecified in the banner. Each bar comes from the most recent request that specifies it:
SystemBars(SystemBarContent.Dark) // screen: both bars dark
if (showBanner) {
SystemBars(statusBar = SystemBarContent.Light) // overlay: status bar light, navigation bar stays dark
DarkBanner()
}Draw mock system bars in previews, design tools or tests by wrapping your content in ProvideSystemBars() and reading the requested icons from its SystemBarsState. Nothing is applied to the platform inside it:
val systemBarsState = remember { SystemBarsState() }
ProvideSystemBars(systemBarsState) {
App()
}
FakeStatusBar(icons = systemBarsState.appearance.statusBar)SystemBars needs an Activity or dialog window. Elsewhere, such as in a service overlay, it does nothing and logs a warning once. Previews skip the warning.Light on dark backgrounds, Dark on light ones.SystemBarsViewController, or create a SystemBarsState(viewController) and return its statusBarStyle from that controller. Without a host, SystemBars does nothing and logs a warning once.