Skip to content

EditText nodes invisible for empty Flutter TextFields (UiAutomator hintText API gap) #340

@rob88pt

Description

@rob88pt

Describe the bug

Empty Flutter TextField widgets are invisible to mobile_list_elements_on_screen on Android. The underlying android.widget.EditText nodes exist in the accessibility tree and are usable via TalkBack, but they do not appear in mobile-mcp's element list, so they cannot be tapped, typed into, or asserted against.

Root cause is the standard adb shell uiautomator dump API used at src/android.ts:481. Since Flutter 3.7+, InputDecoration.labelText is rendered via Android's setHintText() rather than setText() (this was done intentionally to improve TalkBack support — see flutter/flutter#119326). UiAutomator's standard XML dump does not include hintText. The resulting node has empty text, empty content-desc, no resource-id, and is not checkable, so it fails the filter at src/android.ts:337 and is dropped from the elements list.

The same failure mode affects vanilla adb shell uiautomator dump, pre-#897 Maestro, and stock Appium UiAutomator2.

To Reproduce

Minimal Flutter app:

import 'package:flutter/material.dart';

void main() => runApp(const MaterialApp(home: Repro()));

class Repro extends StatelessWidget {
  const Repro({super.key});
  @override
  Widget build(BuildContext ctx) => const Scaffold(
        body: Padding(
          padding: EdgeInsets.all(24),
          child: TextField(decoration: InputDecoration(labelText: 'Name')),
        ),
      );
}
  1. flutter run on a real Android device or emulator (Android 9+, any Flutter 3.7+).
  2. Call mobile_list_elements_on_screen via the MCP.
  3. Observe no EditText node in the returned list.
  4. Call mobile_click_on_screen_at_coordinates on the field's pixel centroid → tap lands but no programmatic way to discover the field first.

Expected behavior

mobile_list_elements_on_screen returns the EditText with a label of "Name" (the value of InputDecoration.labelText), so it can be discovered and acted on by name rather than pixel coordinates.

Actual behavior

The EditText does not appear in the element list at all. The only nearby nodes that survive the filter are siblings carrying content-desc (e.g. suffix icons or external Text labels), none of which are tappable for text input.

Comparison with Maestro (works)

Maestro patched their own dumper in mobile-dev-inc/maestro#897 (merged March 2023). Their serializer at maestro-android/src/androidTest/java/dev/mobile/maestro/ViewHierarchy.kt reads getHintText() via AccessibilityNodeInfo and emits it as a hintText attribute on each node, alongside text and accessibilityText.

Against the same Flutter screen, maestro hierarchy returns the EditText with hintText populated:

{
  "attributes": {
    "text": "",
    "accessibilityText": "",
    "hintText": "Name",
    "clickable": "true",
    "bounds": "[104,670][976,850]",
    "enabled": "true",
    "class": "android.widget.EditText",
    "important-for-accessibility": "true"
  },
  "children": []
}

mobile-mcp's getUiAutomatorXml() on the same device, same app state, returns no node at those bounds.

Configuration

  • mobile-mcp: latest main
  • Flutter: any 3.7+
  • Android: physical Samsung Galaxy A52s, Android 14 (also reproduces on Pixel emulators)

Suggested fix

Two options, in order of robustness:

  1. Mirror Maestro's approach. Bundle a small androidTest APK (or APK-less instrumentation) that uses AccessibilityNodeInfo.getHintText() to build the hierarchy, then surface hintText as a queryable attribute. collectElements at src/android.ts:337 already checks node.hint — the filter is correct; only the source data is missing. See Maestro's ViewHierarchy.kt as a working reference.
  2. Lower bar: in collectElements, also accept nodes where className === "android.widget.EditText" regardless of text/content-desc, so empty EditTexts at least surface as coordinate-tappable. Doesn't give a label, but prevents the node from disappearing entirely.

Happy to open a PR for (2) if useful as a stopgap.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions