Skip to content

Commit 8e05ec6

Browse files
committed
fix(mobile): keyboard dismissing on input focus in auth screens
The KeyboardAvoidingView (behavior='padding') wrapping the ScrollView fought the ScrollView's own iOS keyboard auto-scroll — on tap the keyboard came up then immediately dismissed. Drop the KAV and let the ScrollView inset itself natively via automaticallyAdjustKeyboardInsets (keeping keyboardShouldPersistTaps). Also remove two focus-stealing re-render churns on open: stabilize the Animated.event scroll handler (useRef) and guard the hero onLayout so an unchanged height doesn't trigger a re-render. Stretchy header still works.
1 parent 60c8b14 commit 8e05ec6

1 file changed

Lines changed: 22 additions & 10 deletions

File tree

mobile/src/components/AuthScaffold.tsx

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ReactNode, useRef, useState } from 'react'
2-
import { View, Text, KeyboardAvoidingView, Platform, Animated } from 'react-native'
2+
import { View, Text, Platform, Animated } from 'react-native'
33
import { LinearGradient } from 'expo-linear-gradient'
44
import { StatusBar } from 'expo-status-bar'
55
import { SafeAreaView } from 'react-native-safe-area-context'
@@ -40,26 +40,38 @@ export function AuthScaffold({
4040
outputRange: [2, 1],
4141
extrapolateRight: 'clamp',
4242
})
43+
// Stable scroll handler — recreating Animated.event each render re-attaches the
44+
// native scroll listener, which can interrupt an input's focus as the keyboard opens.
45+
const onScroll = useRef(
46+
Animated.event([{ nativeEvent: { contentOffset: { y: scrollY } } }], {
47+
useNativeDriver: Platform.OS !== 'web', // RN-web has no native driver
48+
}),
49+
).current
4350

4451
return (
45-
<KeyboardAvoidingView
46-
style={{ flex: 1, backgroundColor: colors.base }}
47-
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
48-
>
52+
// Plain container — no KeyboardAvoidingView. Its 'padding' behavior fights the
53+
// ScrollView's own keyboard auto-scroll on iOS and can bounce the tapped input out
54+
// from under the touch (keyboard flashes up, then dismisses). Instead we let the
55+
// ScrollView inset itself for the keyboard natively via automaticallyAdjustKeyboardInsets.
56+
<View style={{ flex: 1, backgroundColor: colors.base }}>
4957
{/* Hero is always the brand gradient (dark), so the status bar stays light. */}
5058
<StatusBar style="light" />
5159
<Animated.ScrollView
5260
contentContainerStyle={{ flexGrow: 1 }}
5361
keyboardShouldPersistTaps="handled"
62+
automaticallyAdjustKeyboardInsets
5463
showsVerticalScrollIndicator={false}
55-
onScroll={Animated.event([{ nativeEvent: { contentOffset: { y: scrollY } } }], {
56-
useNativeDriver: Platform.OS !== 'web', // RN-web has no native driver
57-
})}
64+
onScroll={onScroll}
5865
scrollEventThrottle={16}
5966
>
6067
{/* HERO — gradient bg is absolute so it can stretch independently of the text */}
6168
<View
62-
onLayout={(e) => setHeroH(Math.max(1, Math.round(e.nativeEvent.layout.height)))}
69+
onLayout={(e) => {
70+
// Guard: only update on a real height change so the keyboard-open layout
71+
// pass doesn't trigger a needless re-render (another focus-stealer).
72+
const h = Math.max(1, Math.round(e.nativeEvent.layout.height))
73+
setHeroH((prev) => (Math.abs(prev - h) > 1 ? h : prev))
74+
}}
6375
style={{ paddingBottom: 46 }}
6476
>
6577
<Animated.View
@@ -172,6 +184,6 @@ export function AuthScaffold({
172184
{children}
173185
</View>
174186
</Animated.ScrollView>
175-
</KeyboardAvoidingView>
187+
</View>
176188
)
177189
}

0 commit comments

Comments
 (0)