Skip to content

Elements in Page in android is not accessible #53585

Description

@madarsoftware

Description

Hello, im using React Native Expo, with SDK 53
I'm facing a weird issue that only happens in my android device, works fine on my iphone.

The issue is that nothing is clickable, more like the screen is frozen. I really want to solve this issue but couldn't find any sources that can fix this issue, i tried removing my ThemeScrollView, ThemeContainer, Touchablewithoutfeedback and the screen's elements (buttons, text, input...) is not accessible

Here is the page (login.jsx):
`import * as Haptics from "expo-haptics";
import { Stack, useRouter } from "expo-router";
import { EnvelopeOpenIcon, EnvelopeSimpleIcon, LockKeyIcon } from "phosphor-react-native";
import { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import {
Image,
Keyboard,
TouchableOpacity,
TouchableWithoutFeedback,
useColorScheme
} from "react-native";
import Animated, {
useAnimatedStyle,
useSharedValue,
withTiming,
} from "react-native-reanimated";
import i18n from "../assets/i18n/i18n";
import LoginInfo from "../components/LoginInfo";
import ThemeBottomSheet from "../components/ThemeBottomSheet";
import ThemeButton from "../components/ThemeButton";
import ThemeContainer from "../components/ThemeContainer";
import ThemeInput from "../components/ThemeInput";
import ThemeMawridBackground from "../components/ThemeMawridBackground";
import ThemeScrollView from "../components/ThemeScrollView";
import ThemeText from "../components/ThemeText";
import ThemeView from "../components/ThemeView";
import useToastStore from "../components/Toast/useToastStore";
import useMStore from "../store";
import useAuthStore from "../store/authStore";
import { useProfileTheme } from "../useProfileTheme";

export default function Login() {
const scheme = useColorScheme();
const router = useRouter();
const {
user,
loading,
emailSent,
setEmailSent,
sendOTP,
finishedInitEmail,
setFinishedInitEmail,
verifyOTP,
email,
} = useAuthStore();

const { t } = useTranslation();
const [inputEmail, setInputEmail] = useState("");
const [otpCode, setOtpCode] = useState("");
const [buttonLoading, setButtonLoading] = useState(false);
const otpInputRef = useRef(null);
const fadeAnim = useSharedValue(0);
const btmRef = useRef(null);

const { setPersonType, theme } = useMStore();
const profile = useProfileTheme();
const currentTheme = theme === "auto" ? scheme : theme;
const showToast = useToastStore((s) => s.showToast);

// redirect after login
useEffect(() => {
if (!loading && user && finishedInitEmail) {
router.replace("/HomePage");
}
}, [user, loading]);

// animate transition between email & otp
useEffect(() => {
fadeAnim.value = withTiming(emailSent ? 1 : 0, { duration: 300 });
if (emailSent) {
setTimeout(() => otpInputRef.current?.focus(), 350);
}
}, [emailSent]);

const emailStyle = useAnimatedStyle(() => ({
opacity: withTiming(1 - fadeAnim.value, { duration: 300 }),
display: fadeAnim.value === 1 ? "none" : "flex",
}));

const otpStyle = useAnimatedStyle(() => ({
opacity: withTiming(fadeAnim.value, { duration: 300 }),
display: fadeAnim.value === 0 ? "none" : "flex",
}));

const openBtmSheet = () => btmRef.current.expand();

const handleSendOTP = async () => {
setButtonLoading(true);
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);

if (!inputEmail.toLowerCase().endsWith(".edu.sa")) {
  setButtonLoading(false);
  openBtmSheet();
  showToast(
    "error",
    t("authentication.eduEmailOnly") || "الرجاء استخدام بريد جامعي ينتهي بـ .edu.sa",
    9000
  );
  return;
}

try {
  await sendOTP(inputEmail);
  showToast("email-auth", t("authentication.codeSent"), 9000);
  setButtonLoading(false);
  setEmailSent(true);
} catch (error) {
  setButtonLoading(false);
  showToast(
    "security",
    error.message === "Unable to validate email address: invalid format"
      ? t("authentication.invalidEmailFormat")
      : error.message.includes("invalid")
      ? t("authentication.invalidEmail")
      : error.message.includes("request")
      ? t("authentication.waitForNextEmailSecurityPurposes")
      : error.message,
    9000
  );
}

};

const handleVerifyOTP = async () => {
setPersonType("student");
setButtonLoading(true);
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
try {
await verifyOTP(email, otpCode);
setFinishedInitEmail(true);
setButtonLoading(false);
showToast("success", t("authentication.authSuccess"), 3000);
} catch (error) {
setButtonLoading(false);
showToast(
"error",
error.message === "Token has expired or is invalid"
? t("authentication.tokenExpiredOrInvalid")
: error.message,
9000
);
}
};

return (
<>


<TouchableWithoutFeedback accessible={false} onPress={()=>Keyboard.dismiss()}>
<ThemeContainer style={{ padding: 20, justifyContent: "center" }}>
<Stack.Screen options={{ headerShown: false }} />

{/* heading */}
<ThemeText subtext weight="Regular" style={{ fontSize: 24, marginVertical: 0 }}>
{t("commons.loginText")}

<ThemeText
weight="Medium"
style={{
fontSize: 35,
marginVertical: 20,
marginTop: 10,
lineHeight: 40,
}}
>
{emailSent ? t("authentication.enterOTP") : t("commons.login")}

          <Animated.View style={emailStyle}>
            <ThemeInput
              minimal
              disabled={buttonLoading}
              onChange={setInputEmail}
              value={inputEmail}
              language="en"
              NativeText
              weight="Regular"
              withIcon
              icon={EnvelopeSimpleIcon}
              iconSize={28}
              iconTop={18}
              style={{ paddingVertical: 20 }}
              placeholder={t("authentication.email")}
            />
            <ThemeButton
              minimal
              disabled={buttonLoading}
              style={{ marginTop: 14 }}
              loading={buttonLoading}
              textStyle={{ fontSize: 18 }}
              onPress={handleSendOTP}
              title={t("authentication.sendOTP")}
            />
            <TouchableOpacity onPress={() => router.replace("/traineeLogin")}>
              <ThemeText center style={{ marginTop: 20, color: profile?.main_color || '#51B28E' }}>
                {t("commons.trainee")}
              </ThemeText>
            </TouchableOpacity>
          </Animated.View>

          {/* otp stage */}
          <Animated.View style={otpStyle}>
            <ThemeView
              duotone
              style={{
                padding: 10,
                marginBottom: 0,
                borderRadius: 15,
                borderBottomRightRadius: 0,
                borderBottomLeftRadius: 0,
              }}
            >
              <TouchableOpacity
                onPress={() => showToast("info", t("authentication.holdToEditEmail"), 3500)}
                onLongPress={() => setEmailSent(false)}
                style={{
                  flexDirection: "row",
                  alignItems: "center",
                }}
              >
                <EnvelopeOpenIcon
                  color={currentTheme === "dark" ? "#fff" : "#141414"}
                  size={22}
                  style={{ marginHorizontal: 9, opacity: 0.7 }}
                />
                <ThemeText weight="Regular" subtext>
                  {email}
                </ThemeText>
              </TouchableOpacity>
            </ThemeView>
            <ThemeInput
              ref={otpInputRef}
              otp
              minimal
              icon={LockKeyIcon}
              disabled={buttonLoading}
              onChange={setOtpCode}
              value={otpCode}
              language="en"
              NativeText
              keyboardType="numeric"
              weight="Regular"
              withIcon
              iconSize={28}
              iconTop={18}
              style={{
                paddingVertical: 20,
                borderTopRightRadius: 0,
                borderTopLeftRadius: 0,
                borderTopWidth: 0,
              }}
              placeholder={t("authentication.otpCode")}
            />
            <ThemeButton
              themedBackground
              style={{ marginTop: 14 }}
              loading={buttonLoading}
              textStyle={{ fontSize: 18 }}
              onPress={handleVerifyOTP}
              title={t("authentication.verifyOTP")}
            />
            <TouchableOpacity onPress={() => setEmailSent(false)}>
              <ThemeText style={{ textAlign: "center", marginTop: 20 }}>
                {t("authentication.changeEmail")}
              </ThemeText>
            </TouchableOpacity>
          </Animated.View>                      
          <LoginInfo />
        </ThemeContainer>  
      </TouchableWithoutFeedback>
    </ThemeScrollView>
</ThemeContainer>

  <ThemeBottomSheet
    hideIndicator
    viewStyle={{
      padding: 15,
      paddingTop: 5,
      justifyContent: "center",
      alignItems: i18n.language === "ar" ? "flex-end" : "flex-start",
      gap: 5,
    }}
    index={-1}
    ref={btmRef}
  >
    <Image
      resizeMode="contain"
      source={require("../assets/mail.png")}
      style={{ width: 100, height: 100, marginBottom: 15 }}
    />
    <ThemeText size={24} style={{ marginBottom: 5 }}>
      {t("authentication.eduEmailOnlyInstructionTitle")}
    </ThemeText>
    <ThemeText subtext>{t("authentication.eduEmailOnlyInstructionText1")}</ThemeText>
    <ThemeView
      duotone
      style={{
        padding: 5,
        paddingHorizontal: 15,
        borderRadius: 15,
        marginVertical: 10,
      }}
    >
      <ThemeText size={20}>.edu.sa</ThemeText>
    </ThemeView>
    <ThemeText subtext>{t("authentication.eduEmailOnlyInstructionText2")}</ThemeText>
  </ThemeBottomSheet>
</>

);
}
here is my own ThemeContainer component:
import React from 'react';
import { SafeAreaView, useColorScheme, View } from 'react-native';
import useMStore from '../store';

const ThemeContainer = ({ style, children, contentCenter=false, safeAreaView=false, ...rest }) => {
const { theme } = useMStore();
const scheme = useColorScheme();
const currentTheme = theme === 'auto' ? scheme : theme;

const containerStyle = {
  backgroundColor: currentTheme === 'dark' ? '#141414' : '#fff',      
  flexGrow: 1,
  display: 'flex',
  justifyContent: contentCenter ? 'center' : null,
  alignItems: contentCenter ? 'center' : null,      
  ...style,
};

if (safeAreaView){
return <SafeAreaView style={containerStyle} {...rest}>
{children}

}
return (
<View style={containerStyle} {...rest}>
{children}

);
};

export default ThemeContainer;
here is my own ThemeScrollView component:import * as Device from 'expo-device';
import React from 'react';
import {
Keyboard,
KeyboardAvoidingView,
Platform,
ScrollView,
TouchableWithoutFeedback,
useColorScheme,
View
} from 'react-native';
import useMStore from '../store';

const ThemeScrollView = ({
style,
contentCenter=false,
keyboardShouldPersistTaps,
children,
disableScroll=false,
horizontal = false,
inputDynamic = false,
...rest
}) => {
const { theme } = useMStore();
const scheme = useColorScheme();
const currentTheme = theme === 'auto' ? scheme : theme;

const containerStyle = {
display: contentCenter && 'flex',
justifyContent: contentCenter && 'center',
alignItems: contentCenter && 'center',
backgroundColor: currentTheme === 'dark' ? '#141414' : '#fff',
flex: 1,
...style,
};

const isTablet = Device.deviceType === Device.DeviceType.TABLET;

if (inputDynamic && !isTablet) {
return (
<KeyboardAvoidingView
style={containerStyle}
behavior={Platform.OS === 'ios' ? 'height' : 'height'}
keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 0}
>

<ScrollView
scrollEnabled={!disableScroll}
horizontal={horizontal}
contentContainerStyle={containerStyle}
keyboardShouldPersistTaps={keyboardShouldPersistTaps || "handled"}
{...rest}
>
{children}



);
}

return (
<KeyboardAvoidingView
style={containerStyle}
behavior={Platform.OS === 'ios' ? 'height' : 'height'}
keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 0}
>

<ScrollView
horizontal={horizontal}
scrollEnabled={!disableScroll}
contentContainerStyle={containerStyle}
keyboardShouldPersistTaps={keyboardShouldPersistTaps || "handled"}
{...rest}
>
{children}



);
};

export default ThemeScrollView;
`

here is my own ThemeInput component:

`import mawrid from '@/mawrid.js';
import useMStore from '@/store';
import * as Haptics from 'expo-haptics';
import { EyeClosedIcon, EyeIcon } from 'phosphor-react-native';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { TextInput, TouchableOpacity, useColorScheme, View } from 'react-native';
import { useProfileTheme } from '../useProfileTheme.js';
const ThemeInput = ({
style,
placeholder,
ref,
disabled = false,
transparent = false,
withIcon = false,
disableAutoCorrect=true, // default value --> !disableAutoCorrect === false
weight = 'Medium',
NativeText=false,
onFocus,
sideButton,
sideButtonIcon: SideButtonIcon,
sideButtonPressFunction,
otp=false,
sideButtonIconSize,
sideButtonWeight='default',
sideButtonIconRadius = 15,
sideButtonIconWeight,
sideButtonIconTop = 11,
sideButtonRadius,
icon: Icon,
iconSize = 15,
iconWeight = 'regular',
password,
onChange,
value,
minimal = false,
iconTop = 17,
...rest
}) => {
const { theme } = useMStore();
const profile = useProfileTheme()
const {t, i18n} = useTranslation();
const language = i18n.language
const scheme = useColorScheme();
const currentTheme = theme === 'auto' ? scheme : theme;
const colorThemeMode = currentTheme;
const colors = mawrid.themeColors[colorThemeMode];
const [showPassword, setShowPassword] = useState(false);

const inputStyle = {
backgroundColor: !transparent
? currentTheme === 'dark'
? '#1a1a1a'
: '#fff'
: null,
color: colors.text,
fontFamily: !NativeText ? weight : 'sans-serif',
padding: 10,
minWidth: 100,
borderWidth: 1.5,
borderColor: currentTheme==='dark'?'#282828':'#DADADA50',
maxwidth: minimal && 500,
opacity: disabled ? 0.4 : 1,
paddingVertical: 20,
textAlign: i18n.language === 'ar' ? 'right' : 'left',
...(password && withIcon
? {
paddingLeft: 55, // leave space for icon
paddingRight: 55, // leave space for eye
}
: language === 'ar' && withIcon
? {
paddingRight: 55,
paddingLeft: 15,
}
: language === 'en' && withIcon
? {
paddingLeft: 55,
paddingRight: 15,
}
: {
paddingLeft: 15,
paddingRight: 15,
}),
flex: 1,
fontWeight: 400,
fontSize: 18,
borderRadius: 20,
...style
};

const toggleShowPassword = async () => {
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); // subtle feedback
setShowPassword(!showPassword);
};

return (
<View
style={{
backgroundColor: 'transparent',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
}}
>
<TextInput
autoCorrect={!disableAutoCorrect}
maxLength={otp ? 6 : undefined}
editable={!disabled}
style={inputStyle}
onFocus={onFocus}
ref={ref}
placeholderTextColor={colors.placeholder}
placeholder={placeholder}
onChangeText={onChange}
value={value}
secureTextEntry={password && !showPassword}
{...rest}
/>
{/* icon /}
{withIcon && Icon && (
<Icon
weight={iconWeight}
size={iconSize}
style={{
position: 'absolute',
right: language === 'ar' ? 17 : null,
left: language === 'en' ? 17 : null,
top: iconTop,
color: currentTheme === 'dark' ? '#aaa' : '#80',
}}
/>
)}
{/
side button */}
{
!password && sideButton &&
<TouchableOpacity
activeOpacity={mawrid.appConstants.activeOpacity}
onPress={async() => {
sideButtonPressFunction()
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); // subtle feedback
}}
style={{
backgroundColor: sideButtonWeight === 'fill' ? colors.primary : 'transparent',
padding: 10,
borderRadius: sideButtonIconRadius || 1,
position: 'absolute',
right: language === 'en' ? 8 : null,
left: language === 'ar' ? 8 : null,
top: sideButtonIconTop,
}}>
<SideButtonIcon size={sideButtonIconSize || 24} weight={sideButtonIconWeight || 'regular'} style={{
color: sideButtonWeight === 'fill' ? currentTheme === 'light' ? '#fff' : '#000' : profile?.main_color
}} />

}
{password && (
<TouchableOpacity
style={{
position: 'absolute',
right: language === 'en' ? 14 : null,
left: language === 'ar' ? 14 : null,
top: 12,
padding: 5,
}}
onPress={toggleShowPassword}
>
{!showPassword ? (
<EyeClosedIcon
weight="regular"
size={24}
style={{
color: currentTheme === 'dark' ? '#aaa' : '#80',
marginTop: iconTop - 15.5
}}
/>
) : (
<EyeIcon
weight="regular"
size={24}
style={{
color: currentTheme === 'dark' ? '#aaa' : '#80',
marginTop: iconTop - 15.5
}}
/>
)}

)}

);
};

export default ThemeInput;
`

Steps to reproduce

.

React Native Version

0.79.5

Affected Platforms

Runtime - iOS, Runtime - Android

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

System:
  OS: macOS 15.6
  CPU: (8) arm64 Apple M2
  Memory: 117.45 MB / 8.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 22.12.0
    path: /usr/local/bin/node
  Yarn: Not Found
  npm:
    version: 11.1.0
    path: /usr/local/bin/npm
  Watchman: Not Found
Managers:
  CocoaPods: Not Found
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 24.2
      - iOS 18.2
      - macOS 15.2
      - tvOS 18.2
      - visionOS 2.2
      - watchOS 11.2
  Android SDK: Not Found
IDEs:
  Android Studio: Not Found
  Xcode:
    version: 16.2/16C5032a
    path: /usr/bin/xcodebuild
Languages:
  Java: Not Found
  Ruby:
    version: 2.6.10
    path: /usr/bin/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 19.0.0
    wanted: 19.0.0
  react-native:
    installed: 0.79.5
    wanted: 0.79.5
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: Not found
  newArchEnabled: Not found
iOS:
  hermesEnabled: Not found
  newArchEnabled: Not found

Stacktrace or Logs

none

MANDATORY Reproducer

none

Screenshots and Videos

Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs: Author FeedbackNeeds: ReproThis issue could be improved with a clear list of steps to reproduce the issue.Platform: AndroidAndroid applications.StaleThere has been a lack of activity on this issue and it may be closed soon.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions