Flutter Widget Test Wrapper
When testing a widget, it needs to run in the correct environment, otherwise unrelated errors may occur.
The Widget’s Test Environment
Different widgets have different dependencies, and expect different things in the widget tree.
-
Material widgets expect a
MaterialApp
orScaffold
orMaterial
widget ancestor. -
Widgets that navigate to others using
GoRouter
for example, expectMaterialApp.router
with arouterConfig
. -
A widget that uses
Riverpod
needs aProviderScope
orContainer
ancestor. -
An app in multiple languages probably needs a specific locale set.
-
Other packages may require initialisation, e.g.
Log
.
Supplying the Correct Environment
I use the following wrapper for my widgets under test:
/// Wrap `widget` with a Material app for localizing text, etc.
Widget buildForLocalizedTesting({
required Widget widget,
List<Override> overrides = const <Override>[],
GoRouter? goRouter,
}) {
Log.init();
tz_db.initializeTimeZones();
initializeDateFormatting();
// Initialise Riverpod, using provided overrides
return ProviderScope(
overrides: overrides,
// Provide a `Material` ancestor for material widgets
child: MaterialApp.router(
localizationsDelegates: const <LocalizationsDelegate<dynamic>>[
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const <Locale>[
Locale('en', ''),
],
// Use the provided goRouter, or a minimal default
routerConfig: goRouter ??
GoRouter(
initialLocation: '/',
routes: [
GoRoute(
name: 'root',
path: '/',
builder: (_, __) => Scaffold(body: widget),
),
],
),
),
);
}
This is customised for one of my apps, and should be modified to suite the app you’re testing.
It does the following:
- Wraps the widget in both
MaterialApp
andScaffold
, to ensure that material widgets have a material ancestor, - Provides a default
GoRouter
config for a single route to the widget under test, while also allowing the test to pass in any router config it needs, - Wraps the all the above in
ProviderScope
to initialiseRiverpod
, also allowing the test to pass in any provider overrides it needs, - Initialises the supported locales to be only English (this should probably be another parameter, defaulting to English), and
- Initialises other packages used in the app (
Log
, timezonedatabase, etc).
Leave a comment