Skip to content

feat: Support new edge-to-edge enforcement introduced in Android 15 (API 35) #30090

Closed

Description

@dpalou

Prerequisites

Describe the Feature Request

When running an app targeting SDK 35 in an Android 15 device, now the app fills the whole screen (it no longer leaves some margins for the bottom Android buttons for example). This means that some content might be behind the Android buttons, making that content unclickable.

I'm not sure if the best solution is that cordova-android applies margins to the WebView so it doesn't fill the full screen, making it behave similar to how it behaves in Android 14, or it should be Ionic the one handling those margins (I guess using the "safe area"). I'd prefer the latter because it will look similar to a native Android app, that's why I open this issue.

Right now cordova-android only supports SDK 34, it still doesn't support SDK 35.

To reproduce the issue, just add this in your config.xml and run an app in a device with Android 15:

<preference name="android-targetSdkVersion" value="35" />

For now, this change can be disabled by adding an option in the themes.xml file, but this option is temporary and will be removed in the future:

<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>

More information about this change:

https://developer.android.com/develop/ui/views/layout/edge-to-edge
https://developer.android.com/about/versions/15/behavior-changes-15#edge-to-edge

Describe the Use Case

The new edge-to-edge display should be supported, otherwise some content can become unclickable because it's behind the Android buttons.

Describe Preferred Solution

I guess the easiest solution is to handle this case using the "safe area".

Describe Alternatives

No response

Related Code

No response

Additional Information

No response

Activity

  1. moose4lord commented on Dec 23, 2024

    @moose4lord

    I'll chime in too, just to highlight that this needs some attention. I've been experimenting with migrating to SDK 35 to support Android 15 and have run into this edge-to-edge problem as well. Targeting SDK 35 in variables.gradle:

    compileSdkVersion = 35
    targetSdkVersion = 35
    

    And running the Ionic "sidemenu" starter template app in the Android 15 emulator produces this display:
    (I changed the ion-toolbar background color to "warning" (i.e. yellow) for emphasis)
    Screenshot 2024-12-23 at 10 28 26 AM
    Note the Ionic toolbar renders underneath the Android status bar at the top, and the app's main page renders under the navigation bar at the bottom (with gesture navigation turned off). This is Android's edge-to-edge display paradigm.

    As @dpalou mentions you can work-around this issue by adding the windowOptOutEdgeToEdgeEnforcement attribute to android/app/src/main/res/values-v35/styles.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
      <!-- Base application theme. -->
      <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
      </style>
    
      <style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
        <item name="android:background">@null</item>
        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
      </style>
    </resources>
    

    Which produces this display:
    Screenshot 2024-12-23 at 10 55 36 AM

  2. davideshay commented on Dec 25, 2024

    @davideshay

    Just experienced the same problem and the fix above worked. Question, though -- I would think this belongs in the capacitor repository not the ionic one? Seems like the fix might be around Java code to detect the inset sizes and react appropriately (as indicated here: https://developer.android.com/develop/ui/views/layout/edge-to-edge#java. . I'm happy to create the bug there if you agree.

  3. moose4lord commented on Dec 25, 2024

    @moose4lord

    There's already an issue flagged on the capacitor GitHub page ionic-team/capacitor#7804. It was added pretty recently, so not much activity so far.

    I think this should also be flagged in this repository, because using the windowOptOutEdgeToEdgeEnforcement attribute as a temporary work-around should be added to the Ionic Android scaffolding until Capacitor can make some progress with insets.

  4. Tomjech16 commented on Jan 9, 2025

    @Tomjech16

    Any news? There are problems with existing capacitor plugins like splash screen as well, because of new edge-to-edge.

  5. danpercic86 commented on Jan 22, 2025

    @danpercic86

    I have the same issue, the app is unusable starting with Android 15

  6. mfcarneiro commented on Jan 23, 2025

    @mfcarneiro

    @moose4lord this solution worked on my device API 34 but the low-end device with API 29 not. Just for the record. I will try to use the community plugin for the API 29.

  7. danielehrhardt commented on Jan 27, 2025

    @danielehrhardt
    Contributor

    pls fix

  8. mla03 commented on Jan 30, 2025

    @mla03

    Also waiting for a fix.
    Just updated our application to Capacitor 7, using minSDK 31, Target 35, tested on a Samsung Galaxy A23 running 34, the Navigation bar goes above our design. And same goes for the top part, which goes behind the status bar.
    Any quick fix?

  9. AvniAnay commented on Jan 31, 2025

    @AvniAnay

    I am facing the same issue , some times footer is also overlapped

  10. DevDianDankie commented on Feb 1, 2025

    @DevDianDankie

    bump - same

  11. robingenz commented on Feb 6, 2025

    @robingenz
    Contributor

    I've just published the Android Edge-to-Edge Support plugin, which applies an insert to the web view to restore the previous look. You just need to install it, and your done:

    npm i @capawesome/capacitor-android-edge-to-edge-support

    Optionally, you can also change the background color of the status and navigation bar. Let me know if you encounter any problems by creating an issue or discussion in capawesome-team/capacitor-plugins.

  12. moose4lord commented on Feb 7, 2025

    @moose4lord

    Update: A GitHub PR (#7871) in the Capacitor repository has been approved to address this issue. I hacked the changes to the CapacitorWebView.java file and the PR seems to work well.

    This fix has not been released yet, but look for it in the upcoming Capacitor v7.0.2 release. I doubt they will backport the code to Capacitor 6, so you'll probably have to migrate to Capacitor 7 if you haven't done so already.

    Many thanks to @markemer for the fix.

  13. donqemal commented on Feb 10, 2025

    @donqemal

    @moose4lord do we know, when Capacitor v7.0.2 will be released?

  14. moose4lord commented on Feb 10, 2025

    @moose4lord

    I'm not sure when the next release will happen. They tend to make releases on a monthly cadence, so since 7.0.1 was released about 3 weeks ago, I'm guessing it will be pretty soon. On the other hand, there's a nasty bug (#7866) in the current Android dependencies that needs to get worked out before the next release. So who knows?

  15. 22 remaining items

  16. Luise8 commented on Aug 12, 2025

    @Luise8

    This is a solution that won't work in Capacitor 8

    @dennisfcp why? where did you see this?

  17. TiBz0u commented on Aug 20, 2025

    @TiBz0u

    This is a solution that won't work in Capacitor 8

    @dennisfcp why? where did you see this?

    Force will work for me too because margin are calculated through windowInsets (https://github.com/ionic-team/capacitor/blob/a571724c3238c510bc3991413d1a27222cc47685/android/capacitor/src/main/java/com/getcapacitor/CapacitorWebView.java#L58)

    However, the android attribute windowOptOutEdgeToEdgeEnforcement (https://developer.android.com/reference/android/R.attr#windowOptOutEdgeToEdgeEnforcement) is normally deprecated in Android 16 (https://developer.android.com/about/versions/16/behavior-changes-16?hl=fr) [API 36].
    Purpose of this attribute is to keep same behavior as before API 35

    Android 16 will be the default in Capacitor 8.

    That's certainly the point he mention but it's another approach (manually adapt the android theme).

    Don't hesitate to react if I'm wrong :)

  18. markemer commented on Aug 28, 2025

    @markemer
    Member

    In Capacitor 8 this should "just work" but 7 will need one of these 2:

    android: {
    adjustMarginsForEdgeToEdge: "auto"
    }
    
    android: {
    adjustMarginsForEdgeToEdge: "force"
    }
    
  19. Sina7312 commented on Sep 22, 2025

    @Sina7312

    I've fixed this issue this way:

    StatusBar.getInfo().then((info) => {
      document.documentElement.style.setProperty('--ion-safe-area-top', `${info.height ?? 25}px`);
    });
    
  20. TiBz0u commented on Oct 6, 2025

    @TiBz0u

    Hi everyone,

    After struggling with the edge-to-edge to stay on Android 14 behavior, here my final solution.

    There are two steps.

    1. Add the following line to the AppTheme.NoActionBar theme (it's the one loaded by the BridgeActivity")

    <item name="android:windowOptOutEdgeToEdgeEnforcement" tools:targetApi="35">true</item>

    1. Disable overlaysWebView in capacitor.config.js (set to false in capacitor.config.ts)

    plugins: { StatusBar: { overlaysWebView: false } }
    (exemple if you use capacitor.config.ts)

    This function was not automatically call before.... In Capacitor 7 there was a refactoring and now, setOverlaysWebView is called in the StatusBar constructor and make the app looking "edge-to-edge".

    ``
    See https://capacitorjs.com/docs/apis/status-bar#configuration

    Kr.

    TiBz0u

  21. thetaPC commented on Oct 2, 2026

    @thetaPC
    Contributor

    Thanks for the report, and sorry for the delay.

    Verified on 9.0.6. Ionic pads headers, footers, tab bars and other components using --ion-safe-area-*, which defaults to the WebView's env(safe-area-inset-*) values. When Android 15 started drawing apps edge-to-edge, Android WebView versions before 140 reported those values as 0 because of a Chromium bug, so there was nothing to pad by and content ended up under the status and navigation bars.

    This is now supported. Capacitor 8 injects the real insets as --safe-area-inset-* CSS variables, and #30865 made Ionic read those first, falling back to env(). It shipped in v8.7.15. A follow-up, #31343, makes translucent headers pick up the inset when it arrives after the first render, and shipped in v8.8.18.

    To get it, update to Capacitor 8 and Ionic 8.7.15 or later (8.8.18 or later if you use translucent headers). No extra configuration is needed, since Capacitor 8 loads its SystemBars plugin with insetsHandling: 'css' by default.

    A few workarounds from earlier in this thread no longer apply:

    • android.adjustMarginsForEdgeToEdge was a Capacitor 7 option and was removed in Capacitor 8, so setting it has no effect there.
    • android:windowOptOutEdgeToEdgeEnforcement is ignored by Android for apps targeting SDK 36, which is Capacitor 8's default.
    • Setting --ion-safe-area-top manually from StatusBar.getInfo() is no longer needed, only covers the top edge, and overrides the value Ionic now reads for you. Remove it after upgrading.

    If you are on Cordova, cordova-android 15 keeps content out from under the system bars by default. With AndroidEdgeToEdge enabled, Ionic pads correctly on Android WebView 140 and later.

    Since a lot of people have been following this, could someone on Capacitor 8 and Ionic 8.7.15 or later confirm it works in your app? If you still see content under the system bars on the latest versions, please share a minimal reproduction.

  22. robkorv commented on Oct 5, 2026

    @robkorv

    Capacitor 8 and Ionic 8.7.15 or later confirm it works in your app?

    Deployed two apps with Capacitor 8.5.2 and Ionic 9.0.4. I can confirm that it works.
    I removed all the windowOptOutEdgeToEdgeEnforcement usages. Removed overlaysWebView: false from the StatusBar plugin.

    Important that if you don't have a theme that supports dark mode to add this to your "capacitor.config.ts" plugins section. Without it the device controls will be very hard to see for the user.

        SystemBars: {
          // The app is always light-themed, but without this the status/navigation bar icon color
          // defaults to following the device's system dark/light mode instead, so on a device in
          // system dark mode the icons render light-on-light and become nearly invisible.
          style: 'LIGHT',
        },

    Some specific mitigations that I applied. The inline comments are very verbose because of 🤖.

    Extra padding in my "src/theme/variables.css". Without it the device controls will go over the content. This is by no means a use it for everything fix, it works for my specific use cases. Is is a collection of mitigations over both app.

    /*
    ion-content only gets automatic safe-area padding when it's inside an ion-modal; on a regular
    routed page it doesn't reserve any space for the edge-to-edge Android navigation bar (bottom, or a
    side edge in landscape with 3-button navigation), so content can render underneath/behind it.
    Some pages also set the `ion-padding` utility class on their ion-content, which sets its own fixed
    16px --padding-*; that class selector beats a plain `ion-content` element selector on specificity,
    so it has to be matched (and added to, via calc) explicitly here too, or it silently wins and this
    rule does nothing on exactly the pages that need it.
    */
    ion-content {
      --padding-bottom: var(--ion-safe-area-bottom);
      --padding-start: var(--ion-safe-area-left);
      --padding-end: var(--ion-safe-area-right);
    }
    
    ion-content.ion-padding {
      --padding-bottom: calc(var(--ion-padding, 16px) + var(--ion-safe-area-bottom));
      --padding-start: calc(var(--ion-padding, 16px) + var(--ion-safe-area-left));
      --padding-end: calc(var(--ion-padding, 16px) + var(--ion-safe-area-right));
    }
    
    /*
    Drop the bottom safe-area padding from ion-content when an ion-footer directly follows it, because
    that footer already takes care of the safe area.
    
    `footer-toolbar-padding` is a class Ionic itself puts on ion-footer: ion-footer adds it while the
    keyboard is hidden and there's no ion-tab-bar at the bottom of the page, and removes it otherwise.
    While it's present, Ionic gives the footer's last ion-toolbar
    `padding-bottom: var(--ion-safe-area-bottom)`, so the footer covers the unsafe area at the bottom
    of the screen and ion-content ends at the footer's top edge, clear of that area. Our global
    ion-content rules above would add --ion-safe-area-bottom to the content as well, counting it twice
    and leaving a blank gap between the content and the footer.
    */
    ion-content:has(+ ion-footer.footer-toolbar-padding) {
      --padding-bottom: 0px;
    }
    
    ion-content.ion-padding:has(+ ion-footer.footer-toolbar-padding) {
      --padding-bottom: var(--ion-padding, 16px);
    }
    
    /*
    Inside a full-screen ion-modal, Ionic already adds the bottom safe area to ion-content itself (via
    its internal --internal-content-safe-area-padding-bottom, on top of --padding-bottom), so the rule
    above would reserve that space twice. Reset just the bottom back to Ionic's own default there; the
    start/end insets aren't handled by the modal and still apply. Sheet and card modals don't get that
    internal padding, so revisit this if one is ever added.
    */
    ion-modal ion-content {
        --padding-bottom: 0px;
    }
    
    /*
    The top safe area is normally reserved by an ion-header's toolbar. A full-screen ion-modal without
    one would otherwise put its content under the status bar. Ionic sets --ion-safe-area-top to 0px on
    modals that don't touch the top edge (sheet, card, centered ontablets), so this only adds space where
    it's needed.
    */
    ion-modal:not(:has(ion-header)) ion-content {
        --padding-top: var(--ion-safe-area-top);
    }
  23. added and removed
    needs: replythe issue needs a response from the user
    on Oct 5, 2026
  24. thetaPC commented on Oct 5, 2026

    @thetaPC
    Contributor

    Thanks for confirming, @robkorv, and for the SystemBars tip!! Capacitor's default style follows the device's dark mode setting, so style: 'LIGHT' is the right call for an app that is always light-themed.

    The extra ion-content padding covers a separate gap: ion-content on a regular page doesn't reserve safe-area space when there is no header or footer. That is tracked in #30900, so please follow along there. One caution for anyone copying those rules: ion-item, ion-item-divider and ion-list-header already add the left and right safe area themselves, so the --padding-start and --padding-end rules on ion-content pad lists twice in landscape.

    Since this works on Capacitor 8 with Ionic 8.7.15 or later, I'm closing this one. If you still see content under the system bars on the latest versions, please open a new issue with a minimal reproduction.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions