Custom Addon
If the built-in addons do not meet your needs, you can create your own custom Addons. This allows you to extend the functionality of Widgetbook and tailor it to your specific requirements.
Guide
In this guide, we will be creating a "border addon" that allows you to put a border around your use-case.
0. Base structure
Start by creating a new file in your widgetbook project (e.g. border_addon.dart
).
import 'package:flutter/widgets.dart';
import 'package:widgetbook/widgetbook.dart';
class BorderSetting {
const BorderSetting({
required this.width,
required this.color,
});
final int width;
final Color color;
}
class BorderAddon extends WidgetbookAddon<BorderSetting> {
BorderAddon() : super(name: 'Border');
@override
List<Field> get fields {
// TODO
}
@override
BorderSetting valueFromQueryGroup(Map<String, String> group) {
// TODO
}
@override
Widget buildUseCase(
BuildContext context,
Widget child,
BorderSetting setting,
) {
// TODO
}
}
Now let's start by implementing each of the class members step by step.
1. fields
getter
This is a list of fields that describe how the addon addon will be represented in both:
- The Widgetbook UI โ the "Addons" panel.
- The URL query parameters.
In this case we just need two fields to represent the BorderSetting
:
IntSliderField
- to pick an integer value forBorderSetting.width
.ColorField
- to pick a color value forBorderSetting.color
.
@override
List<Field> get fields {
return [
IntSliderField(
name: 'width',
initialValue: 1,
min: 1,
max: 10,
),
ColorField(
name: 'color',
initialValue: const Color(0xFF000000),
),
];
}
2. valueFromQueryGroup
method
This method is responsible for parsing the query parameters back to the addon setting (i.e. BorderSetting
). This can be done by using the valueOf
helper method to extract the values from the query group by giving it the field's name.
@override
BorderSetting valueFromQueryGroup(Map<String, String> group) {
final width = valueOf<int>('width', group)!;
final color = valueOf<Color>('color', group)!;
return BorderSetting(
width: width,
color: color,
);
}
3. buildUseCase
method
This method is responsible for building the use-case widget with the addon applied. In this case, we will wrap the child widget with a Container
that has a border with the given width and color.
@override
Widget buildUseCase(
BuildContext context,
Widget child,
BorderSetting setting,
) {
return Container(
decoration: BoxDecoration(
border: Border.all(
color: setting.color,
width: setting.width.toDouble(),
),
),
child: child,
);
}
4. Using the Addon
Now that we have implemented the BorderAddon
, we can use it in our Widgetbook project. To do this, we need to add it to the addons
list in the Widgetbook
widget.
import 'package:flutter/widgets.dart';
import 'package:widgetbook/widgetbook.dart';
import 'addons/border_addon.dart';
class WidgetbookApp extends StatelessWidget {
const WidgetbookApp({super.key});
@override
Widget build(BuildContext context) {
return Widgetbook(
// ...
addons: [BorderAddon()],
);
}
}
Multi-snapshot Support
Custom addons can be used with Multi Snapshot Reviews by implementing the AddonConfig
interface.
We are using String colorHex
instead of Color color
in the AddonConfig
because we cannot convert color to hex in a constant constructor. And the
constructor needs to be constant to be used in the cloudAddonsConfigs
map.
import 'package:widgetbook_annotation/widgetbook_annotation.dart';
class BorderAddonConfig extends AddonConfig {
const BorderAddonConfig(this.width, this.colorHex)
: super(
'border', // addon's name in kebab-case
'width:$width,color:$colorHex', // comma-separated fields' names/values
);
final int width;
final String colorHex;
}
import 'package:flutter/widgets.dart';
import 'package:widgetbook/widgetbook.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart';
import 'addons/border_addon.dart';
@App(
cloudAddonsConfigs: {
'border black (2)': [
BorderAddonConfig(2, 'ff000000'),
],
'border white (4)': [
BorderAddonConfig(4, 'ffffffff'),
],
},
)
class WidgetbookApp extends StatelessWidget {
const WidgetbookApp({super.key});
@override
Widget build(BuildContext context) {
return Widgetbook(
// ...
addons: [
BorderAddon(),
],
);
}
}