bc_golden_plugin 3.1.1
bc_golden_plugin: ^3.1.1 copied to clipboard
Golden Plugin is a software that enables you to test the UI you develop against a reference image or a design in Figma.
⚜️ bc_golden_plugin ⚜️ #
A flutter package for automated visual QA. #
This is a package that improves native golden tests and adds features according to needings in our development process, such as automating the visual QA manual process.
Also, it is heavily inspired by other packages like golden_toolkit and alchemist.
An example of the usage can be found in the example directory.
Contents #
- Use cases. 👨🏻💻
- Guidelines. 📝
- BcGoldenConfiguration. 🛠
- Window configuration. 📱
- Custom Window Configuration.
- Accessibility. 🦾
- BcGoldenCapture (New Unified API). ⭐
- bcGoldenTest (Legacy). 🏗
- Multi-Step Golden Testing. 🎬
- LocalFileComparatorThreshold. 📈
- Example of usage 🤌🏻.
Use cases 👨🏻💻 #
The main focus of this package is to compare either components or widgets against the designs provided by the Designers Team in Figma. So, to achieve this approach, we've take the native golden test tool and added some features that are going to be explained below.
Let's start with an example of use case, consider the following design:

Normally we need to schedule a review with someone of the design team to make a quality assurance, this could increase the time to market while waiting for that meeting. So, in solution to this problematic, we've come to the conclusion that we could use the Golden Image Testing but with a different approach.
So now consider the following image as the result of the development process:

As you can see there are actually visual differences.
Guidelines 📝 #
- The tests folder should be inside the package test folder.
- The test should be named with _golden_test.dart notation.
BcGoldenConfiguration. 🛠 #
The BcGoldenConfiguration handles the theme provider and theme data used by the application, so in here you can set your themes to run the tests. You could set this configuration in the flutter_test_config.dart like the following:
Future<void> testExecutable(FutureOr<void> Function() testMain) async {
TestWidgetsFlutterBinding.ensureInitialized();
BcGoldenConfiguration bcGoldenConfiguration = BcGoldenConfiguration();
bcGoldenConfiguration.themeData = BcThemeData.lightTheme;
// Set tolerance ratio (e.g. 0.5% tolerance)
bcGoldenConfiguration.setThresholdRatio(0.005);
await loadConfiguration();
await testMain();
}
Or you can also do it by each tests case if you want.
Loading icon fonts that ship inside the package under test #
When the test runs from inside a Flutter package whose own pubspec
declares an icon font (and that icon font is referenced via
IconData(..., fontPackage: '<thisPackageName>')), pass
currentPackage to loadConfiguration so the font is also registered
under the packages/<thisPackageName>/<family> alias that Icon looks
up. Without it, the icons render as empty squares (missing-glyph
placeholders) in the goldens because the manifest entry uses a local
path (no packages/... prefix) and the lookup name doesn't match.
await loadConfiguration(currentPackage: 'package_name');
This is only needed when the test runs from inside the package itself.
Consumers of that package don't need to set it: their
FontManifest.json already lists the font with the packages/<pkg>/...
prefix and loadAppFonts derives the alias automatically.
Window Configuration | Devices 📱 #
The following are the windows configurations that are available in the package to simulate a physical device:
| name | viewport size | pixel ratio |
|---|---|---|
iPhone 8 |
375 x 667 | 2.0 |
iPhone 13 |
390 x 844 | 3.0 |
iPhone 14 Pro max |
430 x 932 | 3.0 |
Pixel 5 |
360 x 764 | 3.0 |
iPad Pro |
1366 x 1024 | 2.0 |
Here are some examples of how are goldens rendered:
| device | example |
|---|---|
iPhone 8 |
![]() |
iPad Pro |
![]() |
Custom Window Configuration #
You can also use a custom window configuration if none of the above are useful for you, here is an example:
bcGoldenTest(
'Test con custom window config data',
(tester) async {
await bcWidgetMatchesImage(
imageName: 'golden',
widget: const HomePage(title: "Flutter Demo Home Page"),
tester: tester,
device: bcCustomWindowConfigData(
name: 'Custom Configuraton',
pixelDensity: 3.0,
size: const Size(375, 828),
),
);
},
);
Accessibility. 🦾 #
For testing accesibility there is a textScaleFactorparameter that will increase the font size depending in the given number, for example:
| iPhone14 normal | iPhone14 with text scale factor |
|---|---|
![]() |
![]() |
BcGoldenCapture (New Unified API) ⭐ #
The new BcGoldenCapture class provides a unified API for golden testing with both single widget captures and multi-step flow captures. This is the recommended approach for new tests.
Single Widget Testing #
For individual widget tests, use BcGoldenCapture.single:
BcGoldenCapture.single(
'My widget test',
(tester) async {
await tester.pumpWidget(MyWidget());
await expectLater(
find.byType(MyWidget),
matchesGoldenFile('goldens/my_widget.png'),
);
},
shouldUseRealShadows: true,
);
Multi-Step Flow Testing #
For testing complete user flows with multiple screens, use BcGoldenCapture.multiple:
BcGoldenCapture.multiple(
'User login flow',
[
GoldenStep(
stepName: 'Login Screen',
widgetBuilder: () => LoginScreen(),
setupAction: (tester) async {
// Setup actions before screenshot
},
),
GoldenStep(
stepName: 'Dashboard',
widgetBuilder: () => DashboardScreen(),
verifyAction: (tester) async {
// Verification actions after screenshot
},
),
],
const GoldenCaptureConfig(
testName: 'user_login_flow',
layoutType: CaptureLayoutType.vertical,
spacing: 16.0,
),
);
Configuration Options #
The GoldenCaptureConfig class allows you to customize how multiple screenshots are combined:
testName: Name of the golden filelayoutType: How screenshots are arranged (vertical,horizontal,grid)spacing: Space between screenshotsmaxScreensPerRow: Maximum screenshots per row (for grid layout)device: Optional device configuration
Internationalization (i18n) and State Management Injection 🌍 #
To test widgets that consume internationalization (context.loc / AppLocalizations) or depend on modern state management solutions (Riverpod ProviderScope, Bloc BlocProvider, GetIt), pass localizationsDelegates, supportedLocales, locale, or an appWrapper:
await bcWidgetMatchesImage(
imageName: 'my_screen_i18n',
widget: const MyScreen(),
tester: tester,
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
locale: const Locale('es'),
appWrapper: (app) => ProviderScope(
overrides: [
userProvider.overrideWith((ref) => MockUser()),
],
child: app,
),
);
You can also pass appWrapper, localizationsDelegates, supportedLocales, and locale directly into BcGoldenCapture.multiple or BcGoldenCapture.animation.
Animation Testing 🎬 #
For testing animations at specific timestamps, use BcGoldenCapture.animation:
BcGoldenCapture.animation(
'Button scale animation',
AnimatedButton(),
[
GoldenAnimationStep(
timestamp: Duration.zero,
frameName: 'start',
),
GoldenAnimationStep(
timestamp: Duration(milliseconds: 150),
frameName: 'scaled',
),
GoldenAnimationStep(
timestamp: Duration(milliseconds: 300),
frameName: 'end',
),
],
GoldenAnimationConfig(
testName: 'button_animation',
totalDuration: Duration(milliseconds: 300),
animationSteps: [...], // Same steps as above
layoutType: CaptureLayoutType.horizontal,
showTimelineLabels: true,
),
animationSetup: (tester) async {
// Trigger the animation
await tester.tap(find.byType(AnimatedButton));
await tester.pump();
},
);
The animation testing feature captures frames at specific moments in your animation timeline, creating a comprehensive visual test that shows the animation's progression. This is particularly useful for:
- UI Transitions: Validating smooth transitions between states
- Loading Animations: Ensuring consistent spinner or progress animations
bcGoldenTest (Legacy) 🏗 #
The is a legacy function that is still supported but deprecated. For new tests, please use BcGoldenCapture.single instead. This function is default tagged with "golden" and also has additional features for the tests, see the code below:
/// ## bcGoldenTest
/// Function to call the golden test, it replaces the testWigets. This functions
/// are tagged with 'golden'.
///
/// * [description] A brief description of the test,
/// * [test] The test itself,
/// * [shouldUseRealShadows] Whether to render shadows or not,
@isTest
void bcGoldenTest(
String description,
Future<void> Function(WidgetTester) test, {
bool shouldUseRealShadows = false,
}) {
testWidgets(
description,
(widgetTester) async {
body() async {
final initialDebugDisableShadowsValue = debugDisableShadows;
debugDisableShadows = !shouldUseRealShadows;
try {
await test(widgetTester);
} finally {
debugDisableShadows = initialDebugDisableShadowsValue;
debugDefaultTargetPlatformOverride = null;
}
}
await body();
},
tags: ['golden'],
);
}
As you can see you can also change the shadows as well the platform to run test in. Here is an example of the usage;
bcGoldenTest(
'<name of the test file>',
(tester) async {
// Test goes here
},
shouldUseRealShadows: true,
);
Multi-Step Golden Testing 🎬 #
The package now supports testing complete user flows by capturing multiple screenshots and combining them into a single golden file. This is particularly useful for testing complex user journeys, onboarding flows, or multi-screen workflows.
Layout Types #
You can choose how screenshots are arranged:
- Vertical: Screenshots stacked vertically
- Horizontal: Screenshots arranged horizontally
- Grid: Screenshots arranged in a grid pattern
Example: Onboarding Flow #
BcGoldenCapture.multiple(
'App onboarding flow',
[
GoldenStep(
stepName: 'Welcome Screen',
widgetBuilder: () => WelcomeScreen(),
),
GoldenStep(
stepName: 'Features Screen',
widgetBuilder: () => FeaturesScreen(),
setupAction: (tester) async {
// Simulate user interaction
await tester.tap(find.text('Next'));
await tester.pump();
},
),
GoldenStep(
stepName: 'Permissions Screen',
widgetBuilder: () => PermissionsScreen(),
),
],
const GoldenCaptureConfig(
testName: 'onboarding_flow',
layoutType: CaptureLayoutType.grid,
maxScreensPerRow: 2,
spacing: 24.0,
),
);
This will generate a single golden file containing all screenshots arranged according to your configuration.
LocalFileComparator & Tolerance Threshold 📈 #
To handle minor cross-platform rendering differences (e.g., macOS vs. Linux CI anti-aliasing), bc_golden_plugin provides an integrated LocalFileComparatorWithThreshold.
Instead of writing custom comparator boilerplate, configure the tolerance threshold in BcGoldenConfiguration or directly in your test setup:
// Set tolerance as a percentage (e.g., 0.5%)
bcGoldenConfiguration.goldenDifferenceThreshold = 0.5;
// Or set using explicit ratio (0.005 = 0.5%)
bcGoldenConfiguration.setThresholdRatio(0.005);
// Control whether visual diffs fail the test (defaults to true)
bcGoldenConfiguration.willFailOnError = true;
When using bcWidgetMatchesImage or BcGoldenCapture, the comparator is initialized automatically with the configured threshold.
Example of usage 🤌🏻 #
Single Widget Test (Recommended) #
Using the new BcGoldenCapture.single API:
BcGoldenCapture.single(
'button_widget_golden',
(tester) async {
await bcWidgetMatchesImage(
imageName: 'button_widget',
widget: ButtonWidget(),
tester: tester,
device: iPhone8,
textScaleFactor: 2.0,
);
},
shouldUseRealShadows: true,
);
Multi-Step Flow Test #
Using the new BcGoldenCapture.multiple API for testing user flows:
BcGoldenCapture.multiple(
'checkout_flow_golden',
[
GoldenStep(
stepName: 'Product List',
widgetBuilder: () => ProductListScreen(),
),
GoldenStep(
stepName: 'Cart',
widgetBuilder: () => CartScreen(),
),
GoldenStep(
stepName: 'Checkout',
widgetBuilder: () => CheckoutScreen(),
),
],
const GoldenCaptureConfig(
testName: 'checkout_flow',
layoutType: CaptureLayoutType.horizontal,
spacing: 16.0,
),
);
Manual Golden Test Example #
You can also use GoldenScreenshot manually, so you are freely to choose
when to capture a screenshot and still have control of your widget:
testWidgets('Manual golden test', (tester) async {
await tester.runAsync(() async {
GoldenScreenshot screenshotter = GoldenScreenshot();
tester.configureWindow(
GoldenDeviceData.iPhone13,
);
await tester.pumpWidget(
TestBase.appGoldenTest(
widget: const HomePage(title: 'Flutter Demo Home Page'),
key: GlobalKey(),
),
);
await tester.pumpAndSettle();
await screenshotter.captureScreenshot();
await tester.tap( // Navigate to other screen
find.byKey(
const Key('button_widget_key'),
),
);
await tester.pumpAndSettle();
await screenshotter.captureScreenshot();
final combinedScreenshot = await screenshotter.combineScreenshots(
GoldenCaptureConfig(
testName: 'manual_golden',
device: GoldenDeviceData.iPhone13,
layoutType: CaptureLayoutType.horizontal,
),
['home', 'another'],
);
await expectLater(
combinedScreenshot,
matchesGoldenFile('goldens/manual_golden.png'),
);
});
});
Legacy API (Deprecated) #
The legacy bcGoldenTest function is still supported but deprecated:
bcGoldenTest(
'button_widget_golden',
(tester) async {
await bcWidgetMatchesImage(
imageName: 'button_widget',
widget: ButtonWidget(),
tester: tester,
device: iPhone8,
textScaleFactor: 2.0,
);
},
shouldUseRealShadows: true,
);


