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.
Installation
dependencies:
flutternaut: ^0.0.1import 'package:flutternaut/flutternaut.dart';Constructors
Flutternaut.button
For interactive controls: ElevatedButton, IconButton, GestureDetector.
Flutternaut.button(
label: 'login_button',
child: ElevatedButton(onPressed: _login, child: Text('Login')),
)Flutternaut.input
For text input fields: TextField, TextFormField.
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.
Flutternaut.text(
label: 'todo_count',
value: '${todos.length} items',
child: Text('${todos.length} items'),
)Flutternaut.item
For list items: ListTile, list rows.
Flutternaut.item(
label: 'todo_text_$index',
value: todo.text,
child: ListTile(title: Text(todo.text)),
)Flutternaut.checkbox
For checkable items: Checkbox, Switch.
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.
Flutternaut(
label: 'drag_target',
container: true,
child: DragTarget<String>(...),
)Reference
| Constructor | Semantics flags | Use for |
|---|---|---|
| Flutternaut(...) | Manual control | Drag targets, scroll containers, generic wrappers |
| Flutternaut.input(...) | — | TextField, TextFormField |
| Flutternaut.button(...) | button: true | ElevatedButton, IconButton, GestureDetector |
| Flutternaut.text(...) | — | Counters, status labels, error messages |
| Flutternaut.item(...) | container: true | ListTile, list items |
| Flutternaut.checkbox(...) | container: true, checked | Checkbox, 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.
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
@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
@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.
// 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:
{
"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:
dart run flutternautThis 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 elementsvalue→Semantics.value— dynamic text readable by the test enginebutton→Semantics.button— marks interactive controlscontainer→Semantics.container— marks semantic containerschecked→Semantics.checked— tracks checkbox/toggle state
Requirements
- Flutter ≥ 3.27.0
- Dart ≥ 3.6.0