Quick Start

Project Bootstrap

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:

text
dart pub global activate widgetbook_cli 4.0.0-beta.13
widgetbook init

Create your first Component

A 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:

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:

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);
  1. Run dart run build_runner build to generate the boilerplate code.

Create your first Story

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 $.

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);

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.

Create your first Scenario

A Scenario is like a golden test of your story with certain args and mode.

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);

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:

dart
final config = Config(
  // ...
  scenarioConfig: ScenarioConfig(
    definitions: [
      ScenarioDefinition(
        name: 'Dark',
        modes: [MaterialThemeMode('Dark', ThemeData.dark())],
      ),
      ScenarioDefinition(
        name: 'Light',
        modes: [MaterialThemeMode('Light', ThemeData.light())],
      ),
    ],
  ),
);