Flutter
Flutter is a first class citizen for Maestro. It can test both pure and hybrid (i.e add-to-app) Flutter mobile apps.
Interacting with widgets by semantics label
Maestro can interact with widgets that have semantics information attached. By default, this includes all widgets that display text (data
in the Text widget, hintText
in the TextField, and so on). You can also attach semantics information to any widget using Flutter's Semantics widget.
Example: Tap on a widget
Given an InkWell
widget with a Text
widget child:
The following command will tap on it:
Some widget, such as Icon
, don't have implicit semantics. In such cases you can often pass a semanticLabel
:
Then the FloatingActionButton
can be interacted with using the following command:
The Icon
widget simply creates a Semantics
widget itself – see the source.
Finally, you can wrap any widget with Semantics:
Example: Enter text in a widget
To enter text in the following text field widget:
Use this command:
Example: Assert a widget is visible
In cases where both semanticLabel
and text label are provided (like above), the semanticLabel
takes precedence. It's recommended to use maestro studio
in such cases to easily identify what label to use.
Interacting with widgets by semantic identifier
When your app grows, testing often becomes harder.
Maybe the app gets multi-language support, and now you have to decide on the language in which you test it. Maybe some of the strings displayed are non-static (e.g. becase of A/B tests). And the sheer number of screens makes tests harder to maintain.
When you start facing these problems, you should consider using the accessibility identifier instead of semantics labels.
This is a new feature that we contributed to Flutter to make it easier to test apps made with it. It's available on the stable channel since Flutter 3.19 (released on February 15th, 2024). To use it, upgrade to the latest stable Flutter release:
Example: Tap on a widget by semantics identifier
Example: Enter text in a widget by semantics identifier
Good practices
Let's say you have a FancyButton
widget in your app. These buttons are important for you, and you want to ensure they always have an accessibility identifier assigned so they can be reliably interacted with using Maestro. The code sample below requires all callers of FancyButton
to pass an accessibility identifier:
This also has the benefit of reducing widget nesting at the call site:
Of course, there's always danger of a developer accidentally not using the FancyButton
widget and defering to the built-in ElevatedButton
. To combat that, we recommend setting up lint rules that forbid using ElevatedButton
and enforce replacing it with a FancyButton
instead. For example you can use the leancode_lint package with the following configuration in analysis_options.yaml
:
Why not Flutter keys?
Flutter widget keys cannot be used in Maestro because there's no linkage between widget keys and Flutter's accessibility bridge system. This makes using Keys impossible since Maestro is accessibility-tree based.
Also, Flutter API docs for the Key class and Widget.key field say it's for "controlling how one widget replaces another (e.g. in a list)". Keys are just not a mechanism for assigning unique IDs to widgets for testing purposes.
We strongly recommend making your app accessible (not just for UI tests, but for all of your users with different needs). When testing at scale, you should also consider using an accessibility identifier.
Here's also a little trick that you may find useful if you really want to use keys in Maestro (using the FancyButton
example from above):
Callers are required to pass a string key:
And you can easily interact with the widget using Maestro:
Known Limitations
Maestro cannot be used to test Flutter Desktop or Flutter Web apps (yet).
Last updated