Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
joeporpeglia opened this issue Dec 12, 2023 · 13 comments
Labels
API: Dimensions Issue: Author Provided Repro This issue can be reproduced in Snack or an attached project. Needs: Triage 🔍 Platform: Android Android applications.

Comments

@joeporpeglia
Copy link

joeporpeglia commented Dec 12, 2023

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
Copy link
Contributor

gedeagas commented Dec 16, 2023

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
Copy link
Contributor

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
Copy link
Author

@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
Copy link
Contributor

gedeagas commented Dec 19, 2023

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

UIView *mainWindow;
mainWindow = RCTKeyWindow();
// We fallback to screen size if a key window is not found.
CGSize windowSize = mainWindow ? mainWindow.bounds.size : screenSize;


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
Copy link

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
Copy link
Contributor

@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
Copy link

@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
Copy link
Author

joeporpeglia commented Jan 12, 2024

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

@Nonio7
Copy link

Nonio7 commented Feb 7, 2024

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
Copy link

Same issue.

@flodlc
Copy link

flodlc commented May 8, 2024

On react-native 0.74.1 it still happens

@soutua
Copy link

soutua commented Jun 4, 2024

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.

kirillzyusko added a commit to kirillzyusko/react-native-keyboard-controller that referenced this issue Jun 13, 2024
## 📜 Description

Implemented own `useWindowDimensions` hook for Android.

## 💡 Motivation and Context

The problem with default implementation of `useWindowDimensions` on
Android is that it doesn't work well with `edge-to-edge` mode and you
can not retrieve an actual size of the screen. Here is a brief
comparison of values captured on my device (Pixel 7 Pro).

Translucent `StatusBar`:

```sh
height: 867.4285888671875 <- own hook
height: 867.4285888671875, y: 0 <- useSafeAreaFrame
height: 891.4285714285714 <- Dimensions.get('screen')
height: 826.2857142857143 <- Dimensions.get('window')
```

Non translucent `StatusBar`:

```sh
height: 867.4285888671875 <- own hook
height: 826.2857055664062, y: 41.14285659790039 <- useSafeAreaFrame
height: 891.4285714285714 <- Dimensions.get('screen')
height: 826.2857142857143 <- Dimensions.get('window')
```

So as you can see it doesn't react properly on the case when `StatusBar`
is translucent and reports incorrect values, which later on causes
incorrect layout calculation in components like `KeyboardAvoidingView`
or `KeyboardAwareScrollView`.

Theoretically we could workaround this problem by original
`useWindowDimensions().height + StatusBar.currentHeight`, but everything
become trickier when we add translucent `navigationBar` (+ translucent
`statusBar`):

```sh
height: 891.4285888671875 <- own hook
height: 891.4285888671875, y: 0 <- useSafeAreaFrame
height: 891.4285714285714 <- Dimensions.get('screen')
height: 826.2857142857143 <- Dimensions.get('window')
```

In this case derived value `useWindowDimensions().height +
StatusBar.currentHeight` (867.4285888671875) still will produce
incorrect value and all calculations will be broken. So I decided to
create own version of the hook which will cover all the cases.

Issue for reference:
facebook/react-native#41918

Closes
#434
#334

## 📢 Changelog

<!-- High level overview of important changes -->
<!-- For example: fixed status bar manipulation; added new types
declarations; -->
<!-- If your changes don't affect one of platform/language below - then
remove this platform/language -->

### JS

- export own `useWindowDimensions` hook;
- started to use own `useWindowDimensions` in `KeyboardAwareScrollView`
and `KeyboardAvoidingView` components;
- added mock for `useWindowDimensions` hook as default RN
implementation;

### Android

- added `WindowDimensionsListener` class;
- added `ThemedReactContext.content` extension;
- added `ThemedReactContext.setupWindowDimensionListener` extension;

## 🤔 How Has This Been Tested?

Tested manually on Pixel 7 Pro (API 34).

Tested on CI via e2e (API 28).

## 📸 Screenshots (if appropriate):

Pixel 7 Pro (Android 14), `KeyboardAwareScrollView`:

### KeyboardAwareScrollView

|Before|After|
|-------|-----|

|![telegram-cloud-photo-size-2-5429580266013318443-y](https://github.com/kirillzyusko/react-native-keyboard-controller/assets/22820318/4874f962-2726-4cd0-ba96-4b57c076b6f5)|![telegram-cloud-photo-size-2-5429580266013318444-y](https://github.com/kirillzyusko/react-native-keyboard-controller/assets/22820318/28a3b276-f8b7-40fa-a17d-bcc4e58b28b7)|

### KeyboardAvoidingView

|Initial|Before|After|
|------|------|-----|

|![telegram-cloud-photo-size-2-5429580266013318470-y](https://github.com/kirillzyusko/react-native-keyboard-controller/assets/22820318/570d0092-c846-4005-97b0-c596169b91f8)|![telegram-cloud-photo-size-2-5429580266013318469-y](https://github.com/kirillzyusko/react-native-keyboard-controller/assets/22820318/8909fee6-aa42-45a3-87b6-65c59831c703)|![telegram-cloud-photo-size-2-5429580266013318471-y](https://github.com/kirillzyusko/react-native-keyboard-controller/assets/22820318/e64c5dfd-cdc1-4cf5-b731-3a25d3a2fde3)|

## 📝 Checklist

- [x] CI successfully passed
- [x] I added new mocks and corresponding unit-tests if library API was
changed
@bryanltobing
Copy link

I keep stumbling upon this issue

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

Unfortunately, this is not responsive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API: Dimensions Issue: Author Provided Repro This issue can be reproduced in Snack or an attached project. Needs: Triage 🔍 Platform: Android Android applications.
Projects
None yet
Development

No branches or pull requests

9 participants