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
MANDATORY Reproducer
none
Screenshots and Videos

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);
};
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")}
);
}
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;
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 infoStacktrace or Logs
MANDATORY Reproducer
none
Screenshots and Videos