flutternaut

A concise Semantics wrapper for Flutter test automation. Wrap your widgets with Flutternaut to make them discoverable by the test engine. Named constructors auto-configure the right semantics flags for common UI patterns.

View on pub.dev →

Installation

yaml
dependencies:
  flutternaut: ^0.0.1
dart
import 'package:flutternaut/flutternaut.dart';

Constructors

Flutternaut.button

For interactive controls: ElevatedButton, IconButton, GestureDetector.

dart
Flutternaut.button(
  label: 'login_button',
  child: ElevatedButton(onPressed: _login, child: Text('Login')),
)

Flutternaut.input

For text input fields: TextField, TextFormField.

dart
Flutternaut.input(
  label: 'email_input',
  child: TextField(controller: _emailController),
)

Flutternaut.text

For dynamic text displays: counters, status labels, error messages. Pass value so the test engine can read the current text content.

dart
Flutternaut.text(
  label: 'todo_count',
  value: '${todos.length} items',
  child: Text('${todos.length} items'),
)

Flutternaut.item

For list items: ListTile, list rows.

dart
Flutternaut.item(
  label: 'todo_text_$index',
  value: todo.text,
  child: ListTile(title: Text(todo.text)),
)

Flutternaut.checkbox

For checkable items: Checkbox, Switch.

dart
Flutternaut.checkbox(
  label: 'check_$index',
  checked: todo.completed,
  child: Checkbox(value: todo.completed, onChanged: _toggle),
)

Default constructor

For elements that don't fit other categories: drag targets, scroll containers, generic wrappers.

dart
Flutternaut(
  label: 'drag_target',
  container: true,
  child: DragTarget<String>(...),
)

Reference

ConstructorSemantics flagsUse for
Flutternaut(...)Manual controlDrag targets, scroll containers, generic wrappers
Flutternaut.input(...)—TextField, TextFormField
Flutternaut.button(...)button: trueElevatedButton, IconButton, GestureDetector
Flutternaut.text(...)—Counters, status labels, error messages
Flutternaut.item(...)container: trueListTile, list items
Flutternaut.checkbox(...)container: true, checkedCheckbox, Switch

All constructors set excludeSemantics: true to prevent child semantics from polluting accessibility IDs.

The description parameter

All constructors accept an optional description for AI context. It is not passed to Semantics — it exists purely as metadata for the generator and AI test authoring.

dart
Flutternaut.text(
  label: 'flow_item_count',
  description: 'Shows total number of items in the list',
  value: '${items.length} items',
  child: Text('${items.length} items'),
)

Most elements don't need it — labels like login_button or email_input are self-explanatory. Use description for ambiguous labels where the AI might not understand the element's purpose.

View Grouping with @FlutternautView

The @FlutternautView annotation groups elements by screen or view in the generated keys file. This tells the AI which labels belong to which screen, so when you say "test the login screen" it knows exactly which elements are relevant.

Usage on StatefulWidget

dart
@FlutternautView('Login')
class LoginScreen extends StatefulWidget {
  const LoginScreen({super.key});

  @override
  State<LoginScreen> createState() => _LoginScreenState();
}

The generator automatically propagates the view annotation from a StatefulWidget to its State class — you only annotate the widget itself.

Usage on StatelessWidget

dart
@FlutternautView('Login')
class LoginForm extends StatelessWidget {
  const LoginForm({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Flutternaut.input(label: 'email_input', child: TextField()),
        Flutternaut.button(label: 'login_button', child: ElevatedButton(...)),
      ],
    );
  }
}

Cross-file grouping

When a screen is split across multiple files, repeat the annotation on each child widget class. The generator uses the annotation value — not the file path — to group elements together.

dart
// lib/screens/login_screen.dart
@FlutternautView('Login')
class LoginScreen extends StatefulWidget { ... }

// lib/widgets/login_form.dart
@FlutternautView('Login')
class LoginForm extends StatelessWidget { ... }

Generated output

Elements inside an annotated class include a view field in the generated JSON:

json
{
  "label": "email_input",
  "type": "input",
  "view": "Login",
  "description": null,
  "file": "lib/screens/login_screen.dart"
}

Key generation

Run the Flutternaut Generator to extract all labels into a flutternaut_keys.json file:

bash
dart run flutternaut

This JSON is fed to the AI so it only targets real elements in your app. See the flutternaut_generator docs for configuration options and output format details.

How it works

Flutternaut wraps Flutter's native Semantics widget:

  • label → Semantics.label — the accessibility ID used by Appium to find elements
  • value → Semantics.value — dynamic text readable by the test engine
  • button → Semantics.button — marks interactive controls
  • container → Semantics.container — marks semantic containers
  • checked → Semantics.checked — tracks checkbox/toggle state

Requirements

  • Flutter ≥ 3.27.0
  • Dart ≥ 3.6.0