Testing Elements in FlutterWidgetTest that are not Rendered on Screen but Exist Offstage, Similar to IndexedStack | okaryo.log

Testing Elements in FlutterWidgetTest that are not Rendered on Screen but Exist Offstage, Similar to IndexedStack



In my personal development, I added a new feature to the Flutter package LazyLoadIndexedStack and released a new version.

While revising the tests for this, I wanted to verify if the expected elements existed within the children of IndexedStack. Normally, for elements in IndexedStack children other than the specified index, they are not rendered on the screen, and a WidgetTest like expect(find.text('expected text'), findsOneWidget); would not pass.

This article will detail how to write tests for cases where the elements are not rendered on screen but their existence still needs to be verified.

Testing Method

In conclusion, pass skipOffstage: false as an argument to the Finder method.

expect(find.text('expected text', skipOffstage: false), findsOneWidget);

Let’s delve deeper.

Regarding the skipOffstage argument, the source code of Finder comments:

If the skipOffstage argument is true (the default), then this skips nodes that are [Offstage] or that are from inactive [Route]s.
ref: https://github.com/flutter/flutter/blob/d0482116e77994122a7e8596f4a6b7078f08e190/packages/flutter_test/lib/src/finders.dart#L80C1-L81C68

(My translation) If the skipOffstage argument is true (the default), this skips nodes that are [Offstage] or from inactive [Route]s.

I wasn’t familiar with Offstage, so I looked it up.

A widget that lays the child out as if it was in the tree, but without painting anything, without making the child available for hit testing, and without taking any room in the parent.

Offstage children are still active: they can receive focus and have keyboard input directed to them.

Animations continue to run in offstage children, and therefore use battery and CPU time, regardless of whether the animations end up being visible.

Offstage can be used to measure the dimensions of a widget without bringing it on screen (yet). To hide a widget from view while it is not needed, prefer removing the widget from the tree entirely rather than keeping it alive in an Offstage subtree.

In summary, widgets that are not rendered on screen but are in a waiting state are stored in Offstage, and probably for performance enhancement, they are skipped during testing.

So, by passing the flag skipOffstage: false as an argument, elements inside Offstage can also be verified in tests.

Before I noticed this argument, I was writing tests like the following. I found the target IndexedStack and directly checked its children. This method is explicit and not bad in itself, but it has a lot of description and poor visibility.

final indexedStack = find.byType(IndexedStack);
expect(indexedStack, findsOneWidget);

final IndexedStack indexedStackWidget = tester.widget(indexedStack) as IndexedStack;
final children = indexedStackWidget.children;

expect(find.text('element1'), findsOneWidget);

bool hasElement2 = children.any((Widget widget) {
  return widget is Center and widget.child is Text and (widget.child as Text).data == 'element2';
expect(hasElement2, isTrue);


I still have much to learn about Flutter widgets.

Related Posts
Related Posts

This site uses Google Analytics.