When Modal Component renders then AppState blur event listener calls in Android
Description
When Modal/Alert component renders in our React Native App the App State goes blur
React Native Version
0.71.6
Output of npx react-native info
info Fetching system and libraries information... System: OS: macOS 12.6.1 CPU: (16) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz Memory: 81.42 MB / 16.00 GB Shell: 5.8.1 - /bin/zsh Binaries: Node: 16.15.0 - ~/.nvm/versions/node/v16.15.0/bin/node Yarn: 1.22.0 - /usr/local/bin/yarn npm: 8.5.5 - ~/.nvm/versions/node/v16.15.0/bin/npm Watchman: 4.9.0 - /usr/local/bin/watchman Managers: CocoaPods: 1.11.3 - /usr/local/bin/pod SDKs: iOS SDK: Platforms: DriverKit 21.4, iOS 16.0, macOS 12.3, tvOS 16.0, watchOS 9.0 Android SDK: Not Found IDEs: Android Studio: 2021.2 AI-212.5712.43.2112.8512546 Xcode: 14.0.1/14A400 - /usr/bin/xcodebuild Languages: Java: 11.0.15 - /usr/bin/javac npmPackages: @react-native-community/cli: Not Found react: 18.2.0 => 18.2.0 react-native: 0.71.6 => 0.71.6 react-native-macos: Not Found npmGlobalPackages: react-native: Not Found
Steps to reproduce
When Modal/Alert component renders in our React Native App the App State goes blur
AppState.addEventListener('blur', callback);
here this callback method is triggering
Snack, code example, screenshot, or link to a repository
/**
- Sample React Native App
- https://github.com/facebook/react-native
- @format */
`/**
-
Sample React Native App
-
https://github.com/facebook/react-native
-
@format */
import React, {useEffect, useState} from 'react'; import { SafeAreaView, StatusBar, Text, useColorScheme, Modal, TouchableOpacity, AppState, View, } from 'react-native'; import {Colors} from 'react-native/Libraries/NewAppScreen'; function App(): JSX.Element { const isDarkMode = useColorScheme() === 'dark'; const [isVisible, setVisible] = useState(false); useEffect(() => { const blurEventListener = AppState.addEventListener('blur', () => { console.log('blur event trigger'); }); const focusEventListener = AppState.addEventListener('focus', () => { console.log('focus event trigger'); }); return () => { focusEventListener && focusEventListener.remove(); blurEventListener && blurEventListener.remove(); }; }, []); const backgroundStyle = { backgroundColor: isDarkMode ? Colors.darker : Colors.lighter, flex: 1, }; return ( <SafeAreaView style={backgroundStyle}> <StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} backgroundColor={backgroundStyle.backgroundColor} /> <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}> <TouchableOpacity onPress={() => setVisible(!isVisible)}> <Text>Open Modal</Text> </TouchableOpacity> </View> <Modal visible={isVisible}> <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}> <TouchableOpacity onPress={() => setVisible(!isVisible)}> <Text>Close Modal</Text> </TouchableOpacity> </View> </Modal> </SafeAreaView> ); } export default App;
`
| :warning: | Unsupported Version of React Native |
|---|---|
| :information_source: | It looks like your issue or the example you provided uses an unsupported version of React Native. Due to the number of issues we receive, we're currently only accepting new issues against one of the supported versions. Please upgrade to latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If you cannot upgrade, please open your issue on StackOverflow to get further community support. |
Same issue with latest React Native version also
⚠️ Unsupported Version of React Native ℹ️ It looks like your issue or the example you provided uses an unsupported version of React Native. Due to the number of issues we receive, we're currently only accepting new issues against one of the supported versions. Please upgrade to latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If you cannot upgrade, please open your issue on StackOverflow to get further community support.
tried in Latest React Native version also
My requirement is like, When App will goes to background i want to hide the Screen content and when the app will comes to foreground i want to show the content by setting and clearing FLAG_SECURE. but focus is changing when Modal/Alert components render in screen
@Override onWindowFocusChanged method is triggering when Modal component triggers
I tried with onPause() and onResume() also but onPause method is triggering with delay so screen content is not hidden when App goes background
Same issue
I have the same issue, as well as the same use case. I did do some deep diving into the Android code, and from my understanding, this seems to be caused by the fact that the React Native Modal component is based off the Android Dialog class: https://github.com/facebook/react-native/blob/714b502b0c7a5f897432dbad388c02d3b75b4689/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ModalHostShadowNode.java
I'm going to keep looking to see if I can figure out a workaround or something.
Was any solution found for this issue? I am facing it currently as well
Facing the same issue as well :( - did anyone find a work around?
Same thing is happening for me too :(
In case people still need help on this. I had the same exact requirements as @ims7inc and found a workaround.
The workaround involved building my own Android native module, and showing a native Android dialog, with the following steps:
- I detected two events: "home" and "recent apps" button presses which put the app in background. The answer in this StackOverflow thread shows a good way of doing that.
- Create a custom class that extends from the Android
Dialogclass. Set this class up so your dialog takes up the full screen and is just a solid background or whatever you want to hide the screen content. Override theonWindowFocusChangedmethod in this class to close the dialog when focus is changed totrue. - Use the button press callbacks from step 1 to add the FLAG_SECURE flag for when "home" is pressed, and to show the dialog from step 2 when "recent apps" button is pressed (so that it overlays any other existing modals). If you're developing for <= Android 11 (Red Velvet Cake and below), then add FLAG_SECURE instead of the dialog.
- Clear the FLAG_SECURE flag using
onResume(). - The dialog will close itself after you come back to foreground from "recents screen" because the implemented
onWindowFocusChanged()will be called, andonResume()is always called when coming back from home (but not necessarily when from recents which is why we need these two different implementations).
If you're wondering why we don't just show the dialog when pressing "home" instead of using FLAG_SECURE, the reason is because there's a delay and the dialog is not shown in time before the app dismisses when pressing home.
If you're wondering why we don't just use FLAG_SECURE for everything, it's because in >= Android 12 (Snow Cone), when pressing the "recents apps" button, I realized that the app isn't actually going in to the background yet (onPause() is not called), so whatever is using the FLAG_SECURE flag won't be called yet. Behavior is different for <= Android 11.
Trying to do this through React led me to nowhere. This was the best approach I could come up with. Hope it helps!
Any proper solution for this?
@andydotdaniel Mind if I ask to share your solution
any work around to this @andydotdaniel @ys-sherzad
On Android, the background state means that the React Native Activity is in background, and not necessary the entire app.
To handle this, I created a package that implements the Android Lifecycle API for React Native: https://github.com/douglasjunior/react-native-applifecycle
Why Use This?
The original AppState API provided by React Native behaves differently between Android and iOS, particularly regarding the background state:
- On iOS, the
backgroundstate signifies that the entire app is in the background.- On Android, the
backgroundstate indicates that the React Native Activity is in the background, which might not necessarily mean the entire app is in the background.By using
react-native-applifecycle, you can handle these differences seamlessly across both platforms, ensuring that the statebackgroundwill be dispatched only when the entire app is in background.
@douglasjunior... you're a legend! 🙌
could this triggering the blur appstate ?