facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
119.51k stars 24.37k forks source link

useWindowDimensions + translucent system ui returns incorrect dimensions on android #41918

Open joeporpeglia opened 11 months ago

joeporpeglia commented 11 months ago

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

gedeagas commented 11 months ago

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?

gedeagas commented 11 months ago

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').

joeporpeglia commented 11 months ago

@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 commented 11 months ago

@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?

@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.

Screenshot_18

chienvuvan99 commented 11 months ago

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. ![Uploading Screen Shot 2023-12-19 at 18.22.23.png…]()

gedeagas commented 11 months ago

@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.

chienvuvan99 commented 11 months ago

@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.

Screen Shot 2023-12-19 at 18 22 23
joeporpeglia commented 10 months ago

@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 ![Screenshot (Jan 12, 2024 5_58_37 PM)](https://github.com/facebook/react-native/assets/1399969/27ce2fca-3ae2-41a0-bfbc-fdd6eec87343)
Nonio7 commented 9 months ago

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",

darwinshameran commented 9 months ago

Same issue.

flodlc commented 6 months ago

On react-native 0.74.1 it still happens

soutua commented 5 months ago

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.

bryanltobing commented 5 months ago

I keep stumbling upon this issue

Dimensions.get('screen').height it is

Unfortunately, this is not responsive.

hamzawii commented 1 month ago

Same problem with galaxy fold 5, when the phone is folded useWindowDimensions.height includes the statusbar's height and when the phone is unfolded it does not. Is there, a work arround for this ? (using Dimensions to get the height does not rerender views).

joeporpeglia commented 1 month ago

@hamzawii I had to implement a custom useWindowDimensions hook that uses the height/width of a root View