Android SDK35 compatibility (e.g. edge2edge) - research
Description
This is an issue/discussion about the adjustments necessary to make the project properly compatible to apps now targeting Android's new SDK35 (in particular, edge-to-edge).
Resources:
- https://developer.android.com/about/versions/15/behavior-changes-15#edge-to-edge
- https://developer.android.com/develop/ui/views/layout/edge-to-edge
- https://github.com/react-native-community/discussions-and-proposals/discussions/827
Tasks
- [x] Fix bottom tab positioning in sdk-35 (#7991, #7992)
- [ ] Compile the necessary product goals (with the reasoning behind them), and through that come up with a clear list of the changes we want to make.
- [ ] Revisit #8025
This implicitly includes the addressing of this message which we've been getting at the google play dashboard, and as I reckon the community's been getting as well:
Your app uses deprecated APIs or parameters for edge-to-edge
One or more of the APIs you use or parameters that you set for edge-to-edge and window display have been deprecated in Android 15. Your app uses the following deprecated APIs or parameters:
androidx.core.view.WindowCompat.setDecorFitsSystemWindows
android.view.Window.setStatusBarColor
android.view.Window.setNavigationBarColor
android.view.Window.getStatusBarColor
android.view.Window.getNavigationBarColor
android.view.Window.setDecorFitsSystemWindows
...
Reasoning and thoughts
-
Project starting point: RNN doesn't currently account for edge-to-edge layout per se. In particular, there's no prop (nor feature) that would allow an app to specify whether to draw under a nav bar. This is unlike the status-bar, for which there is a prop that can toggle that layout config (
drawBehind: true). -
Full support for edge2edge is not strictly under RNNav's responsibility. Rather, it's a joint RNN & app effort -
- In components generally under RNN - in terms of layout, at least (for example, bottom-tabs / FAB), it's up to RNN to adjust.
- Nevertheless adjusting things such as making the status-bar translucent (in order to draw beneath it), or adding bottom-insets at the end of a list of items in some inner screen - make it all a matter concerning each screen in each app separately. For example: https://developer.android.com/develop/ui/views/layout/edge-to-edge#other-tips
Bottom tabs
BT Positioning
Bottom tabs positioning is a highly fundamental issue. It can be fixed strictly on the basis of the insets listener callback's bottom value - The bottom inset is properly provided by the OS as either 0 or the size of the nav bar, depending on the case (i.e. app-target < 35, ==35, and even 35 with android:windowOptOutEdgeToEdgeEnforcement="true")
[!NOTE]
UPDATE: Done (and patch-fixed) in v8.3.0
BT Color
Looks like due to the guidelines, in edge2edge Android tries to align the navigation bar's background color with the color of screen, automatically. When bottom tabs are present, it doesn't make sense - should align it with the bottom tabs' color. Example:
Note: In 3-btn nav Android sets 80% transparency; read below |
... as opposed to -
Discussion:
Should RNNav's navigationBar: { backgroundColor: '..' } option retain its current purpose in light of that?
There are 2 approaches here:
- In the presence of bottom-tabs, ignore the user and force-align with the bottom tabs' bkg-color;
- Since we're in edge2edge mode, the nav-bar's actually set over the user's content and it might make sense to keep allowing them to set to semi-transparent colors.
In case of the latter approach, it doesn't currently work in gesture-nav mode, so this requires fixing ☑️
The 2nd approach aligns better with the RNNav mindset of user customizations done to the full extent, and therefore we should try to go by it. All the more so, the option is applicable in all screens, not just bottom-tab ones.
We select the following, middle-ground solution: By default, replicate the tab-bar's background color onto the nav-bar; Allow for a user override on that via the navigationBar: {backgroundColor} option. This also plays more nicely with the floating tab mode - read below.
-
What still remains a loose end - for the time being, is Android's automatic 80% transparency configuration of the nav-bar in the 3-button navigation flavor. We'd still have to decide on whether to find a design that works with that or a way to override that. Google apps seem to be inconsistent in that sense.
-
drawBehind: Should be seamless, but brings about something to pay attention to - Thetranslucent: trueeffect should be applied on both the tabs and the nav-bar area (!)
BT Floating mode (new)
-
In order to position the tabs properly in "floating" mode (i.e. a
bottomMarginoption applied by the user), the margins should be calculated additively, taking into account both OS' insets and the user's option. -
As for color, while in default mode we should align the color as explained - in "floating" mode it doesn't make sense; We should resort to keeping the nav-bar (or actually, the view below it?) transparent, and allow the user to override using the
navigationBar: {backgroundColor}option. -
to-do
Status bar
-
From the looks of it, the status bar's color cannot be set, as the util functions do not work. The deprecation warnings refer to API's used by those function so that is likely the reason why. We might reimplemented those using (whichever of) Android's replacement APIs (this ought to be helpful to some extent:
val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView)) -
To-do
FAB
Similar to bottom tabs: Need to bump away based on the nav-bar's insets, namely height (portrait) or width (landscape). The same Android insets methods provides that seamlessly, but it does need to be called and taken into account.
User screen content
RNNav can automatically take care of that one case where the "user" content is not meant to be laid out in an immersive (full-screen) way: When bottom-tabs are present (and not auto-hidden).
In all other cases, it's up to the user to fix in 1 of 2 ways, depending on the screens' content:
- Immersive screen layout - with content being displayed under the nav-bar (maybe also the status bar). This typically refers to full-sized scrollable lists, which stretch all the way down to render under a semi-transparent nav-bar. It is up to the screen's owner to set up a list footer that would help prevent the last item from being stuck under the nav-bar. In the RNNav world, this is relevant mostly to: a) apps with no bottom-tabs, b) pushed-over screens without bottom tabs and c) modals (without bottom-tabs...) -- that includes bottom-aligned option-menus from Wix's UI lib. Perhaps this could be sorted out semi-seamlessly via UI libs (at Wix, at least).
- Screens with bottom-latched items (for example, large CTA buttons). These are cases where
react-native-safe-area-contextmight be useful.
From the version supported edge-to-edge, the KeyboardAvoidingView on Android is not working correctly.
The keyboard covers the contents and does not adjust them.
Please check it.
From the version supported edge-to-edge, the
KeyboardAvoidingViewon Android is not working correctly. The keyboard covers the contents and does not adjust them. Please check it.
Thanks, the version doesn't really support edge-to-edge, as explained. Anyways if you could provide more details so we could address that too, it'd be great.
From the version supported edge-to-edge, the
KeyboardAvoidingViewon Android is not working correctly. The keyboard covers the contents and does not adjust them. Please check it.Thanks, the version doesn't really support edge-to-edge, as explained. Anyways if you could provide more details so we could address that too, it'd be great.
We should consider the InputMethod(ime) inset. You can refer to this issue on the React Native repo.
@peteAhn in that react native issue, the KeyboardAvoidView fails to position a React UI component properly. Is any of RNNav's components not honoring the keyboard in the same way? (which one have you noticed?)
@peteAhn in that react native issue, the KeyboardAvoidView fails to position a React UI component properly. Is any of RNNav's components not honoring the keyboard in the same way? (which one have you noticed?)
If the bottom text Input is upon the RNNav, it's hidden by IME when IME is showing. Under normal conditions, the bottom text input position should be positioned above the IME. All navigation elements, such as Stack, Modal, and Overlay, are affected by this issue.
Then I patched the BottomTabsController.java in the Android library myself.
You can refer to my patched code below.
@Override
protected WindowInsetsCompat onApplyWindowInsets(View view, WindowInsetsCompat insets) {
Insets sysInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars());
Insets imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime());
Insets navInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars());
view.setPaddingRelative(0, 0, 0, imeInsets.bottom + sysInsets.bottom - (imeInsets.bottom > 0 ? navInsets.bottom : 0));
return WindowInsetsCompat.CONSUMED;
}
@peteAhn understood, thanks.
Largely moving to the execution phase, track here: #8079
@peteAhn on 2nd thought, I see that based on your calculations this impl. pushes the tab-bar upwards, setting it right above the keyboard. Is this the desired behavior in your app? It doesn't necessarily make much sense to me.
I think maybe a better approach would be to keep the current impl. but return the insets instead of CONSUMED in order for the ime inset to be addressed by other handlers (likely KeyboardAvoidingComponent)