react-native icon indicating copy to clipboard operation
react-native copied to clipboard

Modal's children are rendered in the top left corner for the first few frames when it's opened (Android)

Open pavel-roj opened this issue 8 months ago • 21 comments

Description

When opening the Modal by settings its visibility to true the modal's children are rendered in the top left corner for the first few frames before the children snap to the correct position.

The issue appeared after upgrading our project to the newest version of react-native with newArch enabled. The issue was not present when testing the same project under the older version from which we were updating which was react-native: 0.73.3.

Steps to reproduce

  1. Open the provided expo snack
  2. Select "Android" (the issue is not visible in the web version)
  3. Click the "SHOW MODAL" button
  4. The red box with the text is rendered in the top left corner for the first few frames, after that it is rerendered in the correct position (the center of the screen)

Since Expo Snack tends to be a bit laggy and shown to the user at a lower FPS on Android, the issue might not be immediately noticeable—it could take a few tries to see the problem properly. I've also included a video from my physical device where the issue is clearly visible.

React Native Version

0.78.2

Affected Platforms

Runtime - Android

Output of npx @react-native-community/cli info

System:
  OS: Linux 6.11 Fedora Linux 39 (Workstation Edition)
  CPU: (12) x64 AMD Ryzen 5 7535U with Radeon Graphics
  Memory: 4.17 GB / 13.33 GB
  Shell:
    version: 5.2.26
    path: /bin/bash
Binaries:
  Node:
    version: 22.14.0
    path: ~/.nvm/versions/node/v22.14.0/bin/node
  Yarn:
    version: 1.22.22
    path: /usr/bin/yarn
  npm:
    version: 10.9.2
    path: ~/.nvm/versions/node/v22.14.0/bin/npm
  Watchman:
    version: 20231008.002904.0
    path: /usr/local/bin/watchman
SDKs:
  Android SDK: Not Found
IDEs:
  Android Studio: Not Found
Languages:
  Java:
    version: 17.0.9
    path: /home/pavelroj/.sdkman/candidates/java/current/bin/javac
  Ruby: Not Found
npmPackages:
  "@react-native-community/cli":
    installed: 15.0.1
    wanted: 15.0.1
  react:
    installed: 19.0.0
    wanted: 19.0.0
  react-native:
    installed: 0.78.2
    wanted: 0.78.2
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: true
iOS:
  hermesEnabled: Not found
  newArchEnabled: false

Stacktrace or Logs

No logs are needed since the issue doesn't cause a crash.

Reproducer

https://snack.expo.dev/@pavelroj/rn-modal-flicker-bug?platform=android

Screenshots and Videos

The issue reproduced on a physical device

https://github.com/user-attachments/assets/75e2255e-ac1f-4264-93a5-1d48398e8bee

pavel-roj avatar Apr 02 '25 06:04 pavel-roj

[!TIP] Newer version available: You are on a supported minor version, but it looks like there's a newer patch available - 0.78.2. Please upgrade to the highest patch for your minor or latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If it does not repro, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the most recent releases.

react-native-bot avatar Apr 02 '25 06:04 react-native-bot

[!TIP] Newer version available: You are on a supported minor version, but it looks like there's a newer patch available - undefined. Please upgrade to the highest patch for your minor or latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If it does not repro, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the most recent releases.

react-native-bot avatar Apr 02 '25 06:04 react-native-bot

Updated to 0.78.2 - the issue still persists

pavel-roj avatar Apr 02 '25 07:04 pavel-roj

Hi @pavel-roj I cannot see modal in snack you have shared, if you have missed adding it by mistake can u please update it and share the link again so that issue can be validated quickly

devanshsaini11 avatar Apr 03 '25 08:04 devanshsaini11

@devanshsaini11 My bad, it seems I didn't save it properly. Can you try now? (the link - https://snack.expo.dev/@pavelroj/rn-modal-flicker-bug?platform=android - is same)

pavel-roj avatar Apr 04 '25 10:04 pavel-roj

Same problem here. Using "react-native": "0.76.7". Any idea on how to fix this?

Mako-L avatar Apr 09 '25 21:04 Mako-L

Same problem here. Using "react-native": "0.77.0". Any idea on how to fix this?

LesterWeng avatar Apr 14 '25 08:04 LesterWeng

Same problem here. Using "react-native": "0.77.0". Any idea on how to fix this?

You can fix this by set width、height of child view

const {width: screenWidth, height: screenHeight} = Dimensions.get('window')

<Modal
      transparent
      statusBarTranslucent
      navigationBarTranslucent
      hardwareAccelerated
      animationType="fade"
      onRequestClose={() => {}}>
      <View
        style={{
          width: screenWidth,
          height: screenHeight,
...

LesterWeng avatar Apr 14 '25 08:04 LesterWeng

Same problem here. Using "react-native": "0.77.0". Any idea on how to fix this?

You can fix this by set width、height of child view

const {width: screenWidth, height: screenHeight} = Dimensions.get('window')

<Modal
      transparent
      statusBarTranslucent
      navigationBarTranslucent
      hardwareAccelerated
      animationType="fade"
      onRequestClose={() => {}}>
      <View
        style={{
          width: screenWidth,
          height: screenHeight,
...

It works good with animationType={"fade"}, your fix. Without it sometimes it has a small flash of items badly placed in the upper part of screen. on "react-native": "0.76.7". at least.

Mako-L avatar Apr 14 '25 09:04 Mako-L

I added a PR which has a consistent reproducible using the rn-tester plus a few remarks: https://github.com/facebook/react-native/pull/50704

AndreiCalazans avatar Apr 14 '25 14:04 AndreiCalazans

Is there any fix? The fix above does usually does not work

tarasovladislav avatar Apr 17 '25 12:04 tarasovladislav

This is a real struggle. I have just upgraded on "0.76.8" and now I am seeing this issue in various places of a really large app. Are we going to create a patch for this? Is anyone looking into this? Switching off new architecture does not sound very promising, but it works then.

sarthak-cars24 avatar Apr 22 '25 11:04 sarthak-cars24

@sarthak-cars24

I was able to to fix this by wrapping Modal with View

import { Modal as RNModal } from "react-native";

<ThemedView
      style={{
        width: "100%",
        height: "100%",
        position: "absolute",
        backgroundColor: "transparent",
      }}
    >
      <RNModal
        visible={isOpen}
        transparent
        animationType="fade"
        statusBarTranslucent
        {...rest}
        onOrientationChange={() => {}}
        onRequestClose={onClose}
        supportedOrientations={[
          "portrait",
          "portrait-upside-down",
          "landscape-left",
          "landscape-right",
        ]}
      >
        {content}
      </RNModal>
    </ThemedView>

tarasovladislav avatar Apr 22 '25 11:04 tarasovladislav

Hey @tarasovladislav Thanks for the super quick reply. Let me try this out, can you also explain how did this solve the issue?

sarthak-cars24 avatar Apr 22 '25 12:04 sarthak-cars24

With this workaround Modal content is always appearing correctly. Before it was in the top left corner or positioned correctly but without correctly rendered elements.

Let me know if this worked for you @sarthak-cars24

tarasovladislav avatar Apr 22 '25 12:04 tarasovladislav

Hey @tarasovladislav Thanks for the fix, I can confirm this workaround works.

sarthak-cars24 avatar Apr 22 '25 13:04 sarthak-cars24

Created a small patch file for React Native v0.76.9, I hope its helpful

diff --git a/node_modules/react-native/Libraries/Modal/Modal.js b/node_modules/react-native/Libraries/Modal/Modal.js
index f0cb214..40a10f2 100644
--- a/node_modules/react-native/Libraries/Modal/Modal.js
+++ b/node_modules/react-native/Libraries/Modal/Modal.js
@@ -280,6 +280,7 @@ class Modal extends React.Component<Props, State> {
     };

     return (
+      <View style={styles.modal}>
       <RCTModalHostView
         animationType={animationType}
         presentationStyle={presentationStyle}
@@ -307,6 +308,7 @@ class Modal extends React.Component<Props, State> {
           </ScrollView.Context.Provider>
         </VirtualizedListContextResetter>
       </RCTModalHostView>
+      </View>
     );
   }

@@ -320,6 +322,7 @@ const side = I18nManager.getConstants().isRTL ? 'right' : 'left';
 const styles = StyleSheet.create({
   modal: {
     position: 'absolute',
+    backgroundColor: 'transparent',
   },
   container: {
     /* $FlowFixMe[invalid-computed-prop] (>=0.111.0 site=react_native_fb) This

sarthak-cars24 avatar Apr 22 '25 14:04 sarthak-cars24

Thanks for the reproducer @AndreiCalazans The fix should be this one:

  • https://github.com/facebook/react-native/pull/51048

cortinico avatar May 01 '25 14:05 cortinico

I can confirm it does fix the layout issue. Thank you:

https://github.com/user-attachments/assets/dc62d90b-3ae1-4f8c-b999-f2a29b21747b

AndreiCalazans avatar May 02 '25 13:05 AndreiCalazans

@tarasovladislav Worked for me as well 👍🏼 You are lifesaver, Thank you buddy

react-native: 0.76.9

ganeshmohane avatar May 09 '25 12:05 ganeshmohane

LMAO, I just upgraded react-native to v0.76.9 and I'm so confused why all my modal is broken.

Thanks @tarasovladislav

serpentarius-hollow avatar May 23 '25 09:05 serpentarius-hollow

I have applied the solution which @tarasovladislav is give still my issue is not fixed i am using react-native 0.78.2 version below i have attached my code:

import React, { useEffect } from "react";
import { View, StyleSheet, Text, Modal } from "react-native";
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withRepeat,
  withTiming,
  cancelAnimation,
  Easing,
} from "react-native-reanimated";
import { scale } from "react-native-size-matters";
import appFonts from "../utils/theme";
import Colors from "../theme/colors";

const Loader = ({
  size = scale(40),
  color = Colors.white,
  loading,
  text = "Loading...",
}) => {
  const rotation = useSharedValue(0);

  useEffect(() => {
    if (loading) {
      rotation.value = withRepeat(
        withTiming(360, {
          duration: 400,
          easing: Easing.linear,
        }),
        -1,
        false
      );
    } else {
      cancelAnimation(rotation);
      rotation.value = 0;
    }

    return () => {
      cancelAnimation(rotation);
      rotation.value = 0;
    };
  }, [loading]);

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ rotate: `${rotation.value}deg` }],
  }));

  return (
    <View
      style={{
        width: "100%",
        height: "100%",
        position: "absolute",
        backgroundColor: "transparent",
      }}
    >
      <Modal
        transparent
        visible={loading}
        animationType="fade"
        statusBarTranslucent
      >
        <View style={styles.modalBackground}>
          <View style={styles.loaderView}>
            <Animated.View
              style={[
                styles.spinner,
                animatedStyle,
                {
                  width: size,
                  height: size,
                  borderRadius: size / 2,
                  borderTopColor: color,
                  borderRightColor: color,
                  borderBottomColor: "#ffffff40",
                  borderLeftColor: "#ffffff10",
                },
              ]}
            />
            <Text style={styles.text}>{text}</Text>
          </View>
        </View>
      </Modal>
    </View>
  );
};

const styles = StyleSheet.create({
  modalBackground: {
    flex: 1,
    backgroundColor: Colors.transparentColor,
    justifyContent: "center",
    alignItems: "center",
  },
  loaderView: {
    backgroundColor: Colors.black500,
    padding: scale(25),
    borderRadius: scale(10),
    alignItems: "center",
    elevation: scale(10),
    shadowColor: Colors.white,
    shadowOffset: { width: scale(0), height: scale(0) },
    shadowOpacity: scale(0.7),
    shadowRadius: scale(10),
  },
  spinner: {
    borderWidth: scale(4),
    borderStyle: "solid",
  },
  text: {
    color: Colors.white,
    marginTop: scale(10),
    fontFamily: appFonts.poppins_bold,
    fontSize: scale(12),
  },
});

export default Loader;

Sameed1998 avatar Jul 15 '25 07:07 Sameed1998

我也遇到了这个问题

WenBin0201 avatar Aug 11 '25 08:08 WenBin0201

For those still having this issue, you can try this one: https://medium.com/@varunk.cet1995/solving-the-ios-modal-flicker-issue-in-react-native-8fa447731f4b

Works well with android, too.

Ceft7412 avatar Aug 15 '25 08:08 Ceft7412

Looks like the fix is on 0.79.6 only. @cortinico Why is it not available on 0.81 or 0.82? Is this expected?

athmsenable avatar Oct 10 '25 07:10 athmsenable

Looks like the fix is on 0.79.6 only. @cortinico Why is it not available on 0.81 or 0.82? Is this expected?

It's definitely included in 0.81 and 0.82 as well

cortinico avatar Oct 10 '25 12:10 cortinico

For those still having this issue, you can try this one: https://medium.com/@varunk.cet1995/solving-the-ios-modal-flicker-issue-in-react-native-8fa447731f4b

Works well with android, too.

for the ones who have an issue with the close since they don't use the exact hardware button and they use a custom button, a useEffect will do the job

  useEffect(() => {
    if (!visible) {
      setIsModalContentVisible(false);
    }
  }, [visible]);

ELHart05 avatar Oct 22 '25 18:10 ELHart05