If the built-in knobs do not meet your needs, you can create your own custom Knobs. This allows you to extend the functionality of Widgetbook and tailor it to your specific requirements.
In this guide, we will be creating a "range knob" that allows you to select a range of values using a RangeSlider.
Start by creating a new file in your widgetbook project (e.g. range_knob.dart).
import 'package:flutter/material.dart';
import 'package:widgetbook/widgetbook.dart';
class RangeKnob extends Knob<RangeValues> {
RangeKnob({
required super.label,
required super.initialValue,
});
@override
List<Field> get fields {
// TODO
}
@override
RangeValues valueFromQueryGroup(Map<String, String> group) {
// TODO
}
}Now let's start by implementing each of the class members step by step.
This is a list of fields that describe how the knob will be represented in both:
- The Widgetbook UI โ the "Knobs" panel.
- The URL query parameters.
In this case we need two fields to represent the RangeValues:
DoubleInputField- to pick a double value for the start of the range.DoubleInputField- to pick a double value for the end of the range.
@override
List<Field> get fields => [
DoubleInputField(
name: '$label.min',
initialValue: initialValue.start,
),
DoubleInputField(
name: '$label.max',
initialValue: initialValue.end,
),
];This method is responsible for parsing the query parameters back to the knob value (i.e. RangeValues). 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
RangeValues valueFromQueryGroup(Map<String, String> group) {
final start = valueOf<double>('$label.min', group)!;
final end = valueOf<double>('$label.max', group)!;
return RangeValues(start, end);
}To use the custom knob, we need to create an extension on KnobsBuilder. This extension includes a range method that adds our RangeKnob to the builder.
extension RangeKnobBuilder on KnobsBuilder {
RangeValues range({
required String label,
RangeValues initialValue = const RangeValues(0, 10),
}) =>
onKnobAdded(
RangeKnob(
label: label,
initialValue: initialValue,
),
)!;
}Now that we have implemented the RangeKnob, we can use it in our use cases. Here's an example of how to use it with a RangeSlider widget:
import 'package:flutter/material.dart';
import 'package:widgetbook/widgetbook.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart';
import '../knobs/range_knob.dart';
@UseCase(name: 'CustomRangeSlider', type: RangeSlider)
Widget buildRangeSliderUseCase(BuildContext context) {
return RangeSlider(
values: context.knobs.range(label: 'Range'),
max: 10,
min: 0,
onChanged: (_) {},
);
}Custom knobs can be used with Multi Snapshot Reviews. The configuration varies based on the number of fields in your custom knob.
If your custom knob is a single-field knob, you can use KnobConfig to configure it for multi-snapshot support. Here's an example:
import 'package:widgetbook_annotation/widgetbook_annotation.dart';
@UseCase(
name: 'Default',
type: MyWidget,
cloudKnobsConfigs: {
'custom': [KnobConfig('foo', 'value')],
},
)
Widget buildUseCase(BuildContext context) {
return MyWidget(
foo: context.knobs.myKnob(label: 'foo'),
);
}If your knobs has more than one field, similar to the RangeKnob defined above, you can use MultiFieldKnobConfig to configure it for multi-snapshot support. Here's how you can do it:
import 'package:flutter/material.dart';
import 'package:widgetbook/widgetbook.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart';
import '../knobs/range_knob.dart';
@widgetbook.UseCase(
name: 'CustomRangeSlider',
type: RangeSlider,
cloudKnobsConfigs: {
'same range': [
MultiFieldKnobConfig({
'range.min': 5,
'range.max': 5,
}),
],
'small range': [
MultiFieldKnobConfig({
'range.min': 4,
'range.max': 6,
}),
],
},
)
Widget buildRangeSliderUseCase(BuildContext context) {
return RangeSlider(
values: context.knobs.range(label: 'range'),
max: 10,
min: 0,
onChanged: (_) {},
);
}
