software-mansion / software-mansion/react-native-screens

[Android] No autofill session is ever started for TextInputs on a pushed screen

Open
#4,612 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area:stack-v4 area:stack-v5 area:tabs cr:platform:android cr:repro-provided platform:android repro-provided
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 header versus headerShown: 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
  1. Create a blank Expo SDK 54 app and add expo-router.
  2. Use the three files below.
  3. npx expo prebuild --platform android and build a release APK.
  4. Install a password manager (Bitwarden works) and set it as the system autofill service, or use Google's.
  5. Launch the app. On the initial screen, focus a field. adb shell dumpsys autofill shows a new line under Requests history.
  6. Tap "Go to sign in" to push the second screen. Focus the username field.
  7. adb shell dumpsys autofill shows 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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.