Developing Widget tests for a Flutter application

NOVASOLUTIONS.TECHNOLOGY is engaged in the development, support and maintenance of iOS, Android, PWA mobile applications. We have extensive experience and expertise in publishing mobile applications in popular markets like Google Play, App Store, Amazon, AppGallery and others.
Development and support of all types of mobile applications:
Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1 servicesAll 1735 services
Developing Widget tests for a Flutter application
Medium
~3-5 business days
FAQ
Our competencies:
Development stages
Latest works
  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1050
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    445

Developing Widget Tests for Flutter Applications

Widget tests in Flutter occupy a niche that is usually filled by either unit tests (too granular) or integration tests (too slow) in mobile development. Widget rendering, button tap navigation, provider data display — all of this is verified without running on a physical device, in seconds, through WidgetTester and a synthetic rendering engine.

Common Initial Problems

The most frequent mistake: attempting to write a widget test for a widget that depends on a real BuildContext — for example, one that reads Theme.of(context) and crashes with No MaterialApp found or No Directionality widget. The solution is to always wrap the widget under test in MaterialApp or minimal Directionality:

await tester.pumpWidget(
  MaterialApp(home: MyWidget()),
);

The second problem — RenderFlex overflowed in tests that didn't appear in the debugger. This means the default WidgetTester size (800×600) doesn't match the real device. Fix it through tester.binding.window.physicalSizeTestValue or tester.view.physicalSize = const Size(390, 844) (in current Flutter 3.x API).

Widget Test Architecture

The test file structure mirrors the widget structure: test/widgets/ mirrors lib/widgets/. Each test file corresponds to one widget or one screen.

group('LoginScreen', () {
  testWidgets('shows error when email is invalid', (tester) async {
    await tester.pumpWidget(MaterialApp(home: LoginScreen()));

    await tester.enterText(find.byKey(Key('email_field')), 'not-an-email');
    await tester.tap(find.byKey(Key('submit_button')));
    await tester.pump(); // synchronous frame

    expect(find.text('Enter a valid email'), findsOneWidget);
  });
});

pump() vs pumpAndSettle() — critical distinction. pump() draws one frame. pumpAndSettle() cycles frames until no pending animations remain. On widgets with infinite animations (AnimatedBuilder with repeat: true) pumpAndSettle() will hang forever — use pump(Duration(seconds: 2)).

Mocking Providers and Dependencies

A widget test without mocking dependencies is not a widget test, it's an integration test. For Riverpod override providers through ProviderScope.overrides:

await tester.pumpWidget(
  ProviderScope(
    overrides: [
      userProvider.overrideWithValue(AsyncValue.data(mockUser)),
    ],
    child: MaterialApp(home: ProfileScreen()),
  ),
);

For BLoCBlocProvider with a mock block through mocktail or mockito. For GetIt — register the mock implementation before the test and reset after through tearDown.

Golden Tests

Golden Tests are a separate category. A widget is rendered, and the screenshot is compared to a reference .png in test/goldens/. On first run the reference is generated (flutter test --update-goldens), on subsequent runs any pixel difference breaks the test.

testWidgets('PrimaryButton golden', (tester) async {
  await tester.pumpWidget(
    MaterialApp(
      home: Center(child: PrimaryButton(label: 'Save')),
    ),
  );
  await expectLater(
    find.byType(PrimaryButton),
    matchesGoldenFile('goldens/primary_button.png'),
  );
});

The issue with Golden Tests: they are platform-dependent. Fonts, antialiasing, shadow rendering — all differ on macOS, Linux, and Windows CI. Solution — run golden tests only on a specific platform (canvaskit renderer in CI through flutter test --platform chrome for web, or a fixed Ubuntu Docker image for mobile goldens).

The golden_toolkit package (pub.dev) adds loadAppFonts(), which eliminates rectangles instead of text in references.

Async and Future in Tests

If a widget launches a Future on initialization (e.g., FutureBuilder + HTTP request), the test must control the completion of that future. Without mocking, the network call either fails or hangs.

when(() => mockApiService.getUser()).thenAnswer((_) async => mockUser);

await tester.pumpWidget(/* ... */);
await tester.pump(); // launches FutureBuilder
await tester.pump(Duration.zero); // wait for Future completion

Fake instead of Mock — when behavior is complex. Implement FakeAuthService extends AuthService, override the necessary methods — cleaner than stubbing each call.

What's Included

  • Writing widget tests for all key screens and components
  • Setting up Golden Tests with correct platform for CI
  • Mocking providers (Riverpod, BLoC, Provider, GetIt)
  • Covering edge cases: empty states, errors, loading
  • CI execution setup with coverage reporting

Timeline

3–5 days for a project with a standard set of screens (10–20 widgets). Golden Tests for the entire UI component library — separate estimation. Cost is calculated individually.