Skip to content

feat: add method for interacting with the Flutter integration driver #1123

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

Alpaca00
Copy link
Contributor

@Alpaca00 Alpaca00 commented May 4, 2025

Hi,
This PR adds a method for interacting with the Flutter integration driver. It adds partial filter support and render tree retrieval.

Please let me know if any changes or improvements are needed.
Thanks!


This is an example of the output for this method, based on the rendering of this application

REQUEST of the existing class

flutter_command = FlutterCommand(driver)

render_tree: list = flutter_command.get_render_tree(
    widget_type='LoginScreen'
)

RESPONSE

[44aaaf1f][HTTP] --> POST /session/44aaaf1f-cac5-43bf-86f4-b75225f1d734/execute/sync {"script":"flutter: renderTree","args":[{"widgetType":"LoginScreen"}]}
[44aaaf1f][AppiumFlutterDriver@73d5] Calling AppiumDriver.execute() with args: ["flutter: renderTree",[{"widgetType":"LoginScreen"}],"44aaaf1f-cac5-43bf-86f4-b75225f1d734"]
[44aaaf1f][WD Proxy] Proxying [POST /session/44aaaf1f-cac5-43bf-86f4-b75225f1d734/element/render_tree] to [POST http://127.0.0.1:10000/session/cc0643ee-5ca6-4087-8f34-21d31e2d36ff/element/render_tree] with body: {"widgetType":"LoginScreen"}
[44aaaf1f][WD Proxy] Got response with status 200: {"sessionId":"cc0643ee-5ca6-4087-8f34-21d31e2d36ff","value":[{"type":"LoginScreen","elementType":"StatefulElement","description":"LoginScreen","depth":0,"attributes":{},"visual":{},"state":{},"rect":{"x":0,"y":0,"width":426.6666666666667,"height":928},"children":[{"type":"Scaffold","elementType":"StatefulElement","description":"Scaffold","depth":1,"attributes":{},"visual":{},"state":{},"rect":{"x":0,"y":0,"width":426.6666666666667,"height":928},"children":[{"type":"_ScaffoldScope","elementType":"InheritedElement","description":"_ScaffoldScope","depth":2,"attributes":{},"visual":{},"state":{},"rect":{"x":0,"y":0,"width":426.6666666666667,"height":928},"children":[{"type":"ScrollNotificationObserver","elementType":"StatefulElement","description":"ScrollNotificationObserver","depth":3,"attributes":{},"visual":{},"state":{},"rect":{"x":0,"y":0,"width":426.6666666666667,"height":928},"children":[{"type":"NotificationListener<ScrollMetricsNotification>","elementType":"_NotificationElement<ScrollMetricsNotificati...
[44aaaf1f][AppiumFlutterDriver@73d5] Responding to client with driver.execute() result: [{"type":"LoginScreen","elementType":"StatefulElement","description":"LoginScreen","depth":0,"attributes":{},"visual":{},"state":{},"rect":{"x":0,"y":0,"width":426.6666666666667,"height":928},"children":[{"type":"Scaffold","elementType":"StatefulElement","description":"Scaffold","depth":1,"attributes":{},"visual":{},"state":{},"rect":{"x":0,"y":0,"width":426.6666666666667,"height":928},"children":[{"type":"_ScaffoldScope","elementType":"InheritedElement","description":"_ScaffoldScope","depth":2,"attributes":{},"visual":{},"state":{},"rect":{"x":0,"y":0,"width":426.6666666666667,"height":928},"children":[{"type":"ScrollNotificationObserver","elementType":"StatefulElement","description":"ScrollNotificationObserver","depth":3,"attributes":{},"visual":{},"state":{},"rect":{"x":0,"y":0,"width":426.6666666666667,"height":928},"children":[{"type":"NotificationListener<ScrollMetricsNotification>","elementType":"_NotificationElement<ScrollMetricsNotification>","description":"NotificationListener<ScrollMetricsNotifi...


REQUEST for a non-existent widget

  flutter_command = FlutterCommand(driver)
  render_tree = flutter_command.get_render_tree(
      widget_type='SharedMask', key='LoginButton'
  )
  assert isinstance(
      render_tree, list
  ), f"Render tree is not a list: {type(render_tree)}"

RESPONSE

[8e05ba9b][HTTP] --> POST /session/8e05ba9b-2ac6-46c9-bf12-43a4b12d569f/execute/sync {"script":"flutter: renderTree","args":[{"widgetType":"SharedMask","key":"LoginButton"}]}
[8e05ba9b]-[AppiumFlutterDriver@dd12] Calling AppiumDriver.execute() with args: ["flutter: renderTree",[{"widgetType":"SharedMask","key":"LoginButton"}],"8e05ba9b-2ac6-46c9-bf12-43a4b12d569f"]
[8e05ba9b][WD Proxy] Proxying [POST /session/8e05ba9b-2ac6-46c9-bf12-43a4b12d569f/element/render_tree] to [POST http://127.0.0.1:10000/session/77305e8a-6d51-4f2c-a058-c84770bb34d0/element/render_tree] with body: {"widgetType":"SharedMask","key":"LoginButton"}
[8e05ba9b][WD Proxy] Got response with status 200: {"sessionId":"77305e8a-6d51-4f2c-a058-c84770bb34d0","value":[]}
[8e05ba9b]-[AppiumFlutterDriver@dd12] Responding to client with driver.execute() result: []

REQUEST without parameters to retrieve all widgets from the root

flutter_command = FlutterCommand(driver)
render_tree = flutter_command.get_render_tree()

RESPONSE

[2fa678cb][HTTP] --> POST /session/2fa678cb-657f-446e-8c39-bec3ac579141/execute/sync {"script":"flutter: renderTree","args":[{}]}
[2fa678cb]-[AppiumFlutterDriver@4c96] Calling AppiumDriver.execute() with args: ["flutter: renderTree",[{}],"2fa678cb-657f-446e-8c39-bec3ac579141"]
[2fa678cb][WD Proxy] Proxying [POST /session/2fa678cb-657f-446e-8c39-bec3ac579141/element/render_tree] to [POST http://127.0.0.1:10000/session/4edc4739-202f-441e-972e-6cf031064b3e/element/render_tree] with body: {}
[2fa678cb][WD Proxy] Got response with status 200: {"sessionId":"4edc4739-202f-441e-972e-6cf031064b3e","value":[{"type":"RootWidget","elementType":"RootElement","description":"[root]","depth":0,"attributes":{},"visual":{},"state":{},"rect":{"x":0,"y":0,"width":1280,"height":2784},"children":[{"type":"View","elementType":"StatefulElement","description":"View","depth":1,"attributes":{},"visual":{},"state":{},"rect":{"x":0,"y":0,"width":1280,"height":2784},"children":[{"type":"RawView","elementType":"StatelessElement","description":"RawView","depth":2,"attributes":{},"visual":{},"state":{},"rect":{"x":0,"y":0,"width":1280,"height":2784},"children":[{"type":"_RawViewInternal","elementType":"_RawViewElement","description":"_RawViewInternal-[_DeprecatedRawViewKey TestFlutterView#2a524]","depth":3,"key":"[_DeprecatedRawViewKey TestFlutterView#2a524]","attributes":{},"visual":{},"state":{},"rect":{"x":0,"y":0,"width":1280,"height":2784},"children":[{"type":"_ViewScope","elementType":"InheritedElement","description":"_ViewScope","depth":4,"attributes":{},"visual":{...
[2fa678cb]-[AppiumFlutterDriver@4c96] Responding to client with driver.execute() result: [{"type":"RootWidget","elementType":"RootElement","description":"[root]","depth":0,"attributes":{},"visual":{},"state":{},"rect":{"x":0,"y":0,"width":1280,"height":2784},"children":[{"type":"View","elementType":"StatefulElement","description":"View","depth":1,"attributes":{},"visual":{},"state":{},"rect":{"x":0,"y":0,"width":1280,"height":2784},"children":[{"type":"RawView","elementType":"StatelessElement","description":"RawView","depth":2,"attributes":{},"visual":{},"state":{},"rect":{"x":0,"y":0,"width":1280,"height":2784},"children":[{"type":"_RawViewInternal","elementType":"_RawViewElement","description":"_RawViewInternal-[_DeprecatedRawViewKey TestFlutterView#2a524]","depth":3,"key":"[_DeprecatedRawViewKey TestFlutterView#2a524]","attributes":{},"visual":{},"state":{},"rect":{"x":0,"y":0,"width":1280,"height":2784},"children":[{"type":"_ViewScope","elementType":"InheritedElement","description":"_ViewScope","depth":4,"attributes":{},"visual":{},"state":{},"rect":{"x":0,"y":0,"width":426.6666666666667,"...

text (Optional[str]): The text of the widget to filter by.

Returns:
List[Optional[Dict]]: A list of dictionaries or None values representing the render tree.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might probably be useful to provide some examples of the output

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the main comment with the output examples

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you sure the recent changes have been committed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did you mean that the output examples should be included in the function's docstring?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, one or two would be enough for the returned value docstring

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added example for the returned value in the docstring

@mykola-mokhnach mykola-mokhnach merged commit 635e762 into appium:master May 5, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants