I have a StatefulWidget that does an async call in its initState()
, to build a Widget. When I manually run this, the widget does build quickly.
However, in my test, even if I use await tester.pump()
or await tester.pumpAndSettle()
, the widget doesn't seem to get built, until way after the test has run.
Widget code:
Widget _coolWidget;
@override
void initState() {
super.initState();
_coolWidget = Container(); // If I set this to OverflowBox() my test passes
_buildWidgetFromCanvas();
}
Future<void> _buildWidgetFromCanvas() {
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder);
// ... more canvas drawing code ...
final img = ... // Image.memory() I build from the canvas
if (!mounted) {
print('no longer mounted');
return;
}
setState(() {
print(img);
_coolWidget = OverflowBox(
child: img,
);
print(_coolWidget);
});
}
Test code:
void main() {
testWidgets('''OverflowBox shows up.''', (WidgetTester tester) async {
await _setUp(tester); // Just instantiates my widget in an app
await tester.pumpAndSettle();
expect(find.byType(OverflowBox).evaluate().length, 1);
});
}
An output when I run my test results in:
failed: Error caught by Flutter test framework, thrown running a test.
Expected: <1>
Actual: <0>
But if I set _coolWidget = OverflowBox();
in initState()
, the test passes.
I have other tests that run after this one. After those ones are done, I see the print logging the print(img);
and print(_coolWidget);
from above, and it correctly logs the drawn image.
I also get the no longer mounted
print, but that only happens as the very last print, prior to Flutter's built in (tearDownAll)
.
Setting durations in the pump() and pumpAndSettle() don't seem to change anything.
I'm probably missing something obvious.
Consider using a FutureBuilder
if your build
method relies on async work.
Future<Image> _buildWidgetFromCanvas() {
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder);
// ... more canvas drawing code ...
if (!mounted) {
return null
}
final img = ... // Image.memory() I build from the canvas
return img;
}
FutureBuilder<Image>(
future: _buildWidgetFromCanvas, // a previously-obtained Future<Image> or null
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.active:
case ConnectionState.waiting:
return SizedBox(height: <height>, width: <width>);
case ConnectionState.done:
return OverflowBox(child: snapshot.data);
}
return null; // unreachable
},
)
You can also update your test code like this:
expect(find.byType(OverflowBox), findsOneWidget);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With