Widgetbook v4 introduced a new init command in the CLI to help you set up your Widgetbook workspace quickly.
Run the following command inside your app or design system package directory:
dart pub global activate widgetbook_cli 4.0.0-beta.13
widgetbook initA Component is a representation of a Widget you want to showcase in Widgetbook.
Each Component can have multiple stories.
Let's start by cataloging a simple widget.
Create a new widget in your app, e.g. lib/widgets/custom_button.dart:
import 'package:flutter/material.dart';
class CustomButton extends StatelessWidget {
final String label;
final VoidCallback onPressed;
const CustomButton({
super.key,
required this.label,
required this.onPressed,
});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
child: Text(label),
);
}
}Create a new file widgetbook/lib/custom_button.stories.dart:
import 'package:widgetbook/widgetbook.dart';
import 'package:your_app/widgets/custom_button.dart';
part 'custom_button.stories.g.dart';
const meta = Meta(CustomButton.new);- Run
dart run build_runner buildto generate the boilerplate code.
A story is a variant of a Component.
For example, if the component is a button, its stories can be "primary button", "secondary button", "disabled button", etc.
Now you can start writing stories for the CustomButton component.
A story must start with $.
import 'package:widgetbook/widgetbook.dart';
import 'package:your_app/widgets/custom_button.dart';
part 'custom_button.stories.g.dart';
const meta = Meta(CustomButton.new);
final $Default = _Story(
args: _Args(
label: StringArg('Button'),
onPressed: Arg.fixed(() {}),
),
);The generated _Args class holds one entry per widget parameter.
Any parameter without a usable default, like CustomButton's required onPressed callback, must be supplied through args, so a bare _Story() will not compile for this widget.
Wrap a value in an Arg type such as StringArg to expose it as an adjustable knob in Widgetbook's UI, or in Arg.fixed(...) to pin it (handy for callbacks).
Widgets whose parameters are all optional or have defaults can still use _Story() with no args.
A Scenario is like a golden test of your story with certain args and mode.
import 'package:widgetbook/widgetbook.dart';
import 'package:your_app/widgets/custom_button.dart';
part 'custom_button.stories.g.dart';
const meta = Meta(CustomButton.new);
final $Default = _Story(
args: _Args(
label: StringArg('Button'),
onPressed: Arg.fixed(() {}),
),
scenarios: [
_Scenario(
name: 'Long Label',
modes: [MaterialThemeMode('Light', ThemeData.light())],
args: _Args.fixed(
label: 'This is a very long label',
onPressed: () {},
),
run: (tester, args) async {
await tester.tap(find.text(args.label));
await tester.pumpAndSettle();
expect(find.text(args.label), findsOneWidget);
},
),
],
);To run these scenarios, you need to run flutter test then check your widgetbook/build/.widgetbook folder for the generated screenshots.
If you want to define global scenarios for all stories in you widgetbook, you can define them in your widgetbook.config.dart file:
final config = Config(
// ...
scenarioConfig: ScenarioConfig(
definitions: [
ScenarioDefinition(
name: 'Dark',
modes: [MaterialThemeMode('Dark', ThemeData.dark())],
),
ScenarioDefinition(
name: 'Light',
modes: [MaterialThemeMode('Light', ThemeData.light())],
),
],
),
);
