react-native
react-native copied to clipboard
useWindowDimensions + translucent system ui returns incorrect dimensions on android
Description
The window height doesn't include the navigation bar height when it's displayed as "translucent". This also occurs if you enable edge-to-edge mode, which similarly shows the app UI under the system UI.
Steps to reproduce
Install and run the reproducer project
React Native Version
0.73.0
Affected Platforms
Runtime - Android
Output of npx react-native info
System:
OS: macOS 13.6
CPU: (8) arm64 Apple M1 Pro
Memory: 86.08 MB / 16.00 GB
Shell:
version: "5.9"
path: /bin/zsh
Binaries:
Node:
version: 18.17.1
path: ~/.nvm/versions/node/v18.17.1/bin/node
Yarn:
version: 1.22.19
path: ~/.nvm/versions/node/v18.17.1/bin/yarn
npm:
version: 9.6.7
path: ~/.nvm/versions/node/v18.17.1/bin/npm
Watchman:
version: 2023.09.04.00
path: /opt/homebrew/bin/watchman
Managers:
CocoaPods:
version: 1.11.3
path: /Users/joeporpeglia/.rbenv/shims/pod
SDKs:
iOS SDK:
Platforms:
- DriverKit 23.0
- iOS 17.0
- macOS 14.0
- tvOS 17.0
- watchOS 10.0
Android SDK: Not Found
IDEs:
Android Studio: 2022.2 AI-222.4459.24.2221.10121639
Xcode:
version: 15.0/15A240d
path: /usr/bin/xcodebuild
Languages:
Java:
version: 11.0.17
path: /Users/joeporpeglia/.sdkman/candidates/java/current/bin/javac
Ruby:
version: 2.7.5
path: /Users/joeporpeglia/.rbenv/shims/ruby
npmPackages:
"@react-native-community/cli": Not Found
react:
installed: 18.2.0
wanted: 18.2.0
react-native:
installed: 0.73.0
wanted: 0.73.0
react-native-macos: Not Found
npmGlobalPackages:
"*react-native*": Not Found
Android:
hermesEnabled: true
newArchEnabled: false
iOS:
hermesEnabled: Not found
newArchEnabled: false
Stacktrace or Logs
n/a
Reproducer
https://github.com/joeporpeglia/rn-android-window-dimensions-reproducer
Screenshots and Videos
In the screenshots below the height of the blue view is set to useWindowDimensions().height.
Navigation bar height is excluded on all android devices I tested
Status bar height is excluded on Pixel 5 device
Please correct me if I'm wrong, but i believe this is the intended behavior, @joeporpeglia.
In React Native, useWindowDimensions provides the dimensions of the window (or viewport), which differ from the full screen dimensions. This difference is particularly noticeable in cases where translucent or hidden system UI elements, like the navigation bar, are present.
Internally, useWindowDimensions utilizes the DisplayMetrics object retrieved from context.getResources().getDisplayMetrics(). This method in Android does exclude the navigation bar in its calculations. It returns metrics based on the current display metrics, representing the screen area available to your application. This typically exclude system UI elements such as the navigation bar.
Currently, there isn't a dedicated hook in React Native for directly accessing the full "Screen" dimensions. However, in situations where you need to consider the entire screen size - particularly in apps that use a translucent navigation bar or operate in edge-to-edge mode - I suggest using the following approach:
import { Dimensions } from 'react-native';
const screenWidth = Dimensions.get('screen').width;
const screenHeight = Dimensions.get('screen').height;
This method will provide you with the full dimensions of the screen, inclusive of the areas covered by system UI elements, which is crucial for accurate layout and design in such specific scenarios.
cc @cortinico As a side note, I believe adding a dedicated hook for screen dimensions could be beneficial for users. I'm considering creating such a hook in the core library, tentatively named useScreenDimensions. What do you think?
Another side note: I saw that in a previous issue, someone suggested that if the app is in full-screen mode on Android, the window height should include the status bar and bottom navigation bar.
We could potentially find a way to implement this, but I'm concerned about the potential for unexpected values due to the addition of the status bar and bottom navigation bar.
If you think about it, including the status bar and bottom navigation bar in the window size essentially yields the same result as Dimensions.get('screen'). Perhaps the best approach is to leave it up to the developers to decide when to use Dimensions.get('screen') or Dimensions.get('window').
@gedeagas that's fair if this is the expected behavior for android. It's surprising though, given useWindowDimensions includes the status bar and home indicator on iOS. Does the screen vs window distinction apply to iOS or is that specific to Android?
@gedeagas that's fair if this is the expected behavior for android. It's surprising though, given
useWindowDimensionsincludes the status bar and home indicator on iOS. Does the screen vs window distinction apply to iOS or is that specific to Android?
@joeporpeglia In iOS, windowSize is determined by the bounds of the key window (mainWindow.bounds.size), or, as a fallback, by the screen size. This measurement represents the full dimensions of the window or screen, encompassing areas that may be underlapped by system elements. Crucially, it does not automatically exclude regions occupied by features like the home indicator or the notch.
https://github.com/facebook/react-native/blob/43826facfab8eed7d01a801778d1c477e60730a6/packages/react-native/React/UIUtils/RCTUIUtils.m#L17-L20
The behavior on iOS differs notably because the interface typically lacks a dedicated bottom navigation bar, unlike some Android devices. In iOS devices with a home button, there is no persistent UI element for bottom navigation. On iPhone X and later models, the home indicator is designed to float over app content, unless managed explicitly with the SafeAreaView component.
I'm also having the same problem. I've used useWindowDimensions, Dimensions.get('screen').height, or Dimensions.get('window').height, but it doesn't work. Please help me solve this problem.
@chienvuvan99 Hi, could you please provide more details about the issue you're encountering? Unfortunately, I'm unable to view the screenshot you've mentioned.
To better assist you, it would be greatly helpful if you could outline the steps to reproduce the issue or share a reproducible demo, similar to what @joeporpeglia has provided.
@gedeagas Hi. I have a list of videos, each item in the list has the width and height of the screen, but on Android the height is not correct. I have used useWindowDimensions, Dimensions.get('screen').height, or Dimensions.get('window').height but they do not work.
@gedeagas I'm actually seeing inconsistent dimensions when testing this on different android devices. On a Pixel 5 emulator the status bar height is included in useWindowDimensions().height, but on my Pixel 5 device it's not. Both are running Android 12, so it's unclear what could be causing the discrepancy here.
I ran the same reproducer on my device and this was the rendered output:
Expand
I am having exactly the same issue. Testing on a physical Pixel 4a and an emulated Pixel 4XL (both with Android 13) yields different results for height. one includes the statusbar height in the value, the other one does not. I tried the useWindowDimensions hook and also Dimensions.get('window') as well as Dimensions.get('screen').
"react": "18.2.0", "react-native": "0.73.4", "expo": "^50.0.6",
Same issue.
On react-native 0.74.1 it still happens
Also one aspect of this issue is that the reported window height doesn't change on an Android device when you set the status bar translucent, while according to the documentation at https://reactnative.dev/docs/dimensions it says "For Android the window dimension will exclude the size used by the status bar (if not translucent) and bottom navigation bar". So it should be excluded if not translucent, and included if it is translucent, which makes sense since the zero point of the drawable area changes according to the translucency.
And then another aspect of this is the fact others have mentioned that it depends on the device whether the status bar area is included in the window height or not, which makes it very hard to make calculations based on the window height since you don't know if it includes the status bar or not.
I keep stumbling upon this issue
Dimensions.get('screen').height it is
Unfortunately, this is not responsive.