software-mansion / software-mansion/react-native-screens
[Android] No autofill session is ever started for TextInputs on a pushed screen
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 3.7k
- Forks
- 714
- Avg merge
- 2d 23h
- Merged PRs (30d)
- 71
Description
Description
On Android, a TextInput that lives on a screen pushed onto a native stack never gets an autofill session. The platform does not merely fail to show a suggestion: it never sends a FillRequest to the autofill service at all, so no password manager can offer credentials, and no "save password" prompt appears after a successful sign-in.
The same TextInput, with the same props, works normally on the stack's initial route. That is the whole difference.
This is what makes it hard to spot from the app side: the fields are correctly annotated, the accessibility tree is correct, the keyboard opens, and nothing is logged. The framework simply never asks.
Verified with two different autofill services (Bitwarden and Google's), so this is not service-specific. Measured with adb shell dumpsys autofill, section Requests history:
| Screen | FillRequest recorded |
|---|---|
| initial route of the stack | yes, immediately on focus |
| route pushed onto the same stack | none, ever |
A native Android app on the same emulator, with the same service, records a request instantly, so the device and the service are working.
Expected: focusing an autofillable TextInput starts an autofill session, wherever the screen sits in the navigation stack.
Things that do not change the outcome
importantForAutofill="yes"on the field.- Backgrounding then foregrounding the app. This differs from #3130, where that cycle was reported to unblock autofill.
- Removing the stack animation (
animation: "none"and the default are both affected). - A custom
headerversusheaderShown: false. - Debug build versus release build.
- Which autofill service is installed.
What does change it
Replacing <Stack> with expo-router's <Slot>, which builds its navigation with the same StackRouter but renders only the focused route, without the native screen container. Autofill then works on every screen. That is the workaround we shipped, and it is only viable because those screens needed no stack animation.
Hypothesis
Offered as a hypothesis, not a claim, since I have not instrumented the native side.
View.notifyEnterOrExitForAutoFillIfNeeded defers the autofill notification to the next View.layout() when isLaidOut() is false, setting PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT instead of calling AutofillManager.notifyViewEntered. For a pushed screen, the subtree appears to be mounted and laid out by the Fabric mount batch before the fragment transaction attaches it, and View.onAttachedToWindow clears that laid-out flag. React Native never lays out a view again when its frame has not changed, so the deferred notification never fires.
That would explain why the initial route is unaffected: its fragment is attached before the first layout, not after it.
Steps to reproduce
- Create a blank Expo SDK 54 app and add
expo-router. - Use the three files below.
npx expo prebuild --platform androidand build a release APK.- Install a password manager (Bitwarden works) and set it as the system autofill service, or use Google's.
- Launch the app. On the initial screen, focus a field.
adb shell dumpsys autofillshows a new line underRequests history. - Tap "Go to sign in" to push the second screen. Focus the username field.
adb shell dumpsys autofillshows no new line. No suggestion appears above the keyboard.
To see the contrast in one run, put a TextInput on both screens.
app/_layout.tsx
import { Stack } from "expo-router";
export default function RootLayout() {
return <Stack />;
}
app/index.tsx
import { Link } from "expo-router";
import { Text, View } from "react-native";
export default function Index() {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center", gap: 24 }}>
<Text style={{ fontSize: 20 }}>Autofill repro</Text>
<Link href="/login" style={{ fontSize: 18, color: "#06c", padding: 16 }}>
Go to sign in
</Link>
</View>
);
}
app/login.tsx
import { useState } from "react";
import { StyleSheet, Text, TextInput, View } from "react-native";
export default function Login() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
return (
<View style={styles.page}>
<Text style={styles.h1}>Sign in</Text>
<TextInput
style={styles.input}
value={username}
onChangeText={setUsername}
placeholder="username"
autoComplete="username"
autoCapitalize="none"
autoCorrect={false}
/>
<TextInput
style={styles.input}
value={password}
onChangeText={setPassword}
placeholder="password"
secureTextEntry
autoComplete="password"
autoCapitalize="none"
autoCorrect={false}
/>
</View>
);
}
const styles = StyleSheet.create({
page: { flex: 1, paddingTop: 120, paddingHorizontal: 20, gap: 16, backgroundColor: "#fff" },
h1: { fontSize: 22, fontWeight: "700", marginBottom: 12 },
input: { borderWidth: 1, borderColor: "#bbb", borderRadius: 8, paddingHorizontal: 12, height: 48, fontSize: 16 },
});
Snack or a link to a repository
https://github.com/tomKFM/rn-screens-android-autofill-repro
Screens version
4.16.0
React Native version
0.81.5
Platforms
Android
JavaScript runtime
Hermes
Workflow
Expo bare workflow
Build type
Release mode
Device
Android emulator
Device model
Pixel emulator, Android 17 (API 37), also seen on a production build on physical devices
Acknowledgements
Yes
Related
- #349, open since 2020, same symptom, no reproducer that isolates the pushed screen.
- #3130, reported as a regression of #349. The background and foreground cycle described there does not help in this case.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce with the linked rn-screens-android-autofill-repro using app/_layout.tsx and app/login.tsx, then compare adb shell dumpsys autofill Requests history for initial and pushed routes. Trace the Android view lifecycle around View.notifyEnterOrExitForAutoFillIfNeeded, View.layout, and View.onAttachedToWindow during fragment attachment. Done means pushed TextInputs generate FillRequest and password-save prompts as initial-route fields do.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, react-native, typescript
- Domain
- mobile
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100