Skip to content

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
@KazuCocoa KazuCocoa added the size:S contribution size: S label Jun 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S contribution size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants