Skip to content

Match the status bar icons of the Android modal to the screen behind it - #194

Merged
simonhamp merged 1 commit into
NativePHP:mainfrom
martin-ro:fix/android-modal-status-bar
Nov 2, 2026
Merged

simonhamp merged 1 commit into
NativePHP:mainfrom
martin-ro:fix/android-modal-status-bar

Conversation

@martin-ro

Copy link
Copy Markdown
Contributor

On Android, the status bar icons in a native:modal are always white. In the light theme the modal has a light background, so the clock and the icons are almost invisible. When the modal closes, the screen shows its dark icons again.

We found it in our app: a photo viewer in a modal with :dismissible="false", in the light theme.

Steps to reproduce

use Illuminate\View\View;
use Native\Mobile\Edge\NativeComponent;

class ModalStatusBar extends NativeComponent
{
    public bool $open = false;

    public function show(): void
    {
        $this->open = true;
    }

    public function hide(): void
    {
        $this->open = false;
    }

    public function render(): View
    {
        return view('native.modal-status-bar');
    }
}
<native:column class="w-full h-full items-center justify-center">
    <native:button @tap="show">Open</native:button>

    <native:modal :visible="$open" @dismiss="hide">
        <native:column class="w-full h-full items-center justify-center">
            <native:text>Look at the status bar</native:text>
        </native:column>
    </native:modal>
</native:column>

Set the device to the light theme, open the screen on Android and tap Open.

  • Expected: dark status bar icons, the same as on the screen behind the modal.
  • Actual: white status bar icons on the light background.

status_bar_style in config/nativephp.php has no effect on the modal either. With dark, the screen has dark icons and the modal has white icons.

Cause

ModalRenderer.kt opens a Compose Dialog with decorFitsSystemWindows = false. The dialog has its own window, and that window draws behind the status bar. MainActivity.configureStatusBar() sets the status bar appearance only on the activity's window. Nothing sets it on the dialog's window, so that window keeps the default, which is white icons.

adb shell dumpsys window windows shows this while the modal is open. The activity's window has apr=LIGHT_STATUS_BARS LIGHT_NAVIGATION_BARS. The dialog's window has no apr line.

The dark theme hides the problem, because white icons are correct there.

Changes

  • A SideEffect in the Dialog content reads isAppearanceLightStatusBars from the activity's window and sets it on the dialog's window. The renderer gets the activity from MainActivity.instance, as WebviewRenderer.kt does.
  • The value comes from the activity's window, not from isSystemInDarkTheme(), because the activity applies status_bar_style and that value is private to MainActivity. So auto, light and dark all hold in the modal, and the modal always matches the screen behind it.
  • The SideEffect runs again when the theme changes, so an open modal follows the change.
  • Only the dialog's window changes, so the screen behind the modal needs no reset when the modal closes. The padding and the navigation bar do not change.
  • tests/ModalRendererTest.php gets one test that checks this in the Kotlin source. It fails on the old code.

native:bottom-sheet also has its own window. Its icons were already dark in the light theme, so this change does not touch it.

How I tested

Android 16 emulator (API 36.1), in an app on nativephp/mobile 4.7.0, nativephp/mobile-ui 0.9.0 and Compose UI 1.10.0, with ModalRenderer.kt from 0.9.0 (before) and from this branch (after).

There were two test screens: the component above, and a modal with :dismissible="false" and its own close button. auto ran on both screens, dark and light on the second one. I took a screenshot of each state and read the color of the clock digits from it.

status_bar_style Theme Screen Modal, before Modal, after Screen after close
auto light dark white dark dark
auto dark white white white white
dark light dark white dark dark
dark dark dark white dark dark
light light white not run white white
light dark white not run white white
  • Theme change with the modal open, light to dark and back (adb shell cmd uimode night): the icons follow the theme on both test screens. After the modal closes, the screen has the right icons.
  • Back key on the modal from the steps above: the modal closes and the screen keeps its dark icons.
  • No flash: I recorded the screen while the modal opened and closed twice in the light theme, and read the clock digits in each frame. Before, 38 of 81 frames have white digits. After, 0 of 82.

Not tested: a physical device, Android 13 to 15, and a modal inside a bottom sheet. iOS does not change.

Before Android 15, the system does not force a transparent status bar. I read the code for this case but did not run it. The app theme Theme.AndroidPHP sets android:statusBarColor to transparent, in the light and in the dark variant, and the dialog overlay does not set a color. So the modal's background should still be behind the icons. I read this in the API 36 sources.

Also seen, the same before and after this change: in the dark theme, the plain <native:text> in the modal above is dark on the dark background. I did not look into it, and I did not change it here.

The modal opens a Compose Dialog, which has its own window. Nothing set
the status bar appearance on that window, so its icons stayed white and
were almost invisible on a light background.

Copy the appearance from the activity's window, which applies
status_bar_style.
@simonhamp
simonhamp merged commit 558cc27 into NativePHP:main Nov 2, 2026
4 checks passed
@simonhamp

Copy link
Copy Markdown
Member

thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants