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 story.
0. Base structure
Start by creating a new file in your widgetbook project (e.g. border_addon.dart).
import 'package:flutter/material.dart';
import 'package:widgetbook/widgetbook.dart';
class BorderSetting {
const BorderSetting(this.width, this.color);
final int width;
final Color color;
}
class BorderAddon extends Addon<BorderSetting> {
BorderAddon()
: super(
name: 'Border',
initialValue: const BorderSetting(1, Color(0xFF000000)),
);
@override
List<Field<dynamic>> get fields => [
// ...
]
@override
BorderSetting valueFromQueryGroup(QueryGroup? group) {
// TODO
}
@override
QueryGroup valueToQueryGroup(BorderSetting value) {
// TODO
}
@override
Widget apply(
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: initialValue.width,
min: 1,
max: 10,
),
ColorField(
name: 'color',
initialValue: initialValue.color,
),
];
}
If your addon just has a single filed, you can use the SingleFieldOnly mixin to reduce boilerplate.
With that, you can skip steps 2 and 3 as a default valueFromQueryGroup and valueToQueryGroup implementations will be provided for you.
class BorderAddon extends Addon<BorderSetting> with SingleFieldOnly {
// ...
}
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(QueryGroup? group) {
if (group == null) return initialValue;
return BorderSetting(
valueOf('width', group)!,
valueOf('color', group)!,
);
}
3. valueToQueryGroup method
This method is used to be able to convert Modes to a serializable QueryGroup, so that it can be stored in the URL. To use the same encoding technique used in the fields, you can use the paramOf helper method.
@override
QueryGroup valueToQueryGroup(BorderSetting value) {
return QueryGroup({
'width': paramOf('width', value.width),
'color': paramOf('color', value.color),
});
}
4. apply method
This method is responsible for building the story 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 apply(
BuildContext context,
Widget child,
BorderSetting setting,
) {
return Container(
decoration: BoxDecoration(
border: Border.all(
color: setting.color,
width: setting.width.toDouble(),
),
),
child: child,
);
}
5. 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 'addons/border_addon.dart';
final config = Config(
// ...
addons: [BorderAddon()],
);
Mode
To use this addon for testing, you can enable the BorderMode in your stories:
class BorderMode extends Mode<BorderSetting> {
BorderMode(double width, Color color)
: super(
BorderSetting(width, color),
BorderAddon(),
);
}
final $Default = _Story(
scenarios: [
_Scenario(
name: 'Red Border',
modes: [
BorderMode(2, Colors.red),
]
),
],
)

