Skip to content

fix: resolve issues with safe area / systembars plugin - #8535

Open
tafelnl wants to merge 8 commits into
ionic-team:mainfrom
tafelnl:fix/resolve-issues-with-safe-area
Open

fix: resolve issues with safe area / systembars plugin#8535
tafelnl wants to merge 8 commits into
ionic-team:mainfrom
tafelnl:fix/resolve-issues-with-safe-area

Conversation

@tafelnl

@tafelnl tafelnl commented Jul 21, 2026

Copy link
Copy Markdown

This resolves some (most/all maybe even) current issues with the safe area / systembars plugin. These changes have been battle tested in production for several months now.

Closes #8525
Closes #8528
Closes #8416
Closes ionic-team/capacitor-keyboard#68
Closes ionic-team/capacitor-keyboard#61
Closes ionic-team/capacitor-keyboard#46
Closes ionic-team/capacitor-keyboard#28
Closes ionic-team/capacitor-keyboard#53
Closes ionic-team/capacitor-keyboard#57

And potentially some more. I haven't had time to look at all issues.

We already had a conversation about some of these design approaches earlier on. See #8268 and #8384 (comment)

Comment thread core/system-bars.md

@tafelnl tafelnl Jul 21, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't find documentation on how to (auto)generate this doc. But I think it should be good like this

static final String BAR_STATUS_BAR = "StatusBar";
static final String BAR_GESTURE_BAR = "NavigationBar";

// TODO: In Cap 9, add an additional option "full"

@tafelnl tafelnl Jul 21, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what this was for, but I removed it because I think with the addition of native all use cases are covered

}

@JavascriptInterface
public void onDOMReady() {

@tafelnl tafelnl Jul 21, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dong this only in the onPageCommitVisible listener seems to work equally fine in my testing

if (isSafeAreaPluginPresent()) {
Logger.warn(
"SystemBars",
"You should uninstall `@capacitor-community/safe-area`. Having this library installed can lead to unexpected behavior."

@tafelnl tafelnl Jul 21, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having this warning here seems fair to me, as the two are strictly incompatible and there are still loads of users installing the community plugin

@tafelnl tafelnl Jul 27, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ended up removing this warning. Because, actually, I think it makes more sense to solve this using an updating peerDependencies policy in the plugin itself. Nonetheless it might be good to mention somewhere in the docs that the use of (any) third party safe area plugins is discouraged. And if a developer wants to use such a plugin, that insetsHandling should be set to disable to prevent interference

.build();
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {

@tafelnl tafelnl Jul 21, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still not sure what this if-statement was meant for. I asked about it a few times in other PRs but never got a response, so I dont know the rationale behind it. Seems unnecessary to me. Moreover, it seems to be the root cause of many bug reports

Comment thread cli/src/declarations.ts Outdated
*
* @default false
*/
initialViewportFitCover?: boolean;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is really important to prevent layout jumps

@tafelnl tafelnl Jul 27, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ended up renaming this to initialViewportFitValueHint (in 7abde6f) because it sounds a bit more logical

if (!systemBarsInsetsHandling.equals("disable") && keyboardResizeOnFullScreen) {
Logger.warn(
"SystemBars",
"You should omit `Keyboard.resizeOnFullScreen` in your `capacitor.config.json`. Other values can lead to unexpected behavior."

@tafelnl tafelnl Jul 21, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I mentioned before, I think this config value should be deprecated altogether. If you still want it, I think it makes much more sense to include it in this plugin to prevent future bugs and resolve present bugs

Comment thread cli/src/declarations.ts
* This option is only supported on Android.
*
* @default `DEFAULT`
* @default 'css'

@tafelnl tafelnl Jul 21, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would strongly suggest to make native the default in Capacitor v9 as that would align with how the web and iOS platforms work right now. So I think that is the more expected behavior

}

ViewCompat.setOnApplyWindowInsetsListener((View) getBridge().getWebView().getParent(), (v, insets) -> {
View view = getActivity().getWindow().getDecorView();

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is necessary for the keyboard animation to have the correct background color

}

initWindowInsetsListener();
initSafeAreaCSSVariables();

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was redundant, because it's already being handled in the initWindowInsetsListener

Comment on lines -243 to -244
Insets safeAreaInsets = calcSafeAreaInsets(newInsets);
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I simplified this, because it was doing redundant recalculations which caused bugs across different webview versions

@Vissie2

Vissie2 commented Jul 22, 2026

Copy link
Copy Markdown

@theproducer

@tafelnl tafelnl Jul 24, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to call EdgeToEdge.enable(this) inside BridgeActivity.onCreate (if insetsHandling !== 'disable'). Because it's easy to forget. See #8522 for example.

However, one could argue that it's breaking change. In that case, it should then be documented somewhere, and instead be added in Capacitor v9.

On the other hand, your app will express unexpected behavior when not calling EdgeToEdge.enable(this), and there already were quite a few breaking changes in non-major updates in Capacitor v8 regarding the safe area. So it might make sense to add it nonetheless

@tafelnl
tafelnl force-pushed the fix/resolve-issues-with-safe-area branch from 37861c7 to 281dc12 Compare July 27, 2026 06:41
@tafelnl
tafelnl force-pushed the fix/resolve-issues-with-safe-area branch from c202f7f to 7abde6f Compare July 27, 2026 06:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment