Getting Started with Widgetbook 3
Widgetbook is an open-source package for organizing and testing Flutter widgets in different scenarios. It provides a safe environment for previewing and testing widgets in various states, improving UI debugging and user experience.
Widgetbook 3 brings forth the power of code generation for automatic use case creation and many new enhancements and improvements. In addition, brand new documentation will help Flutter developers to utilize all features of Widgetbook 3 in detail.
Widgetbook 3 highlights
This robust new release introduces innovative features, improved naming conventions, and reduced dependencies. The spotlight is on Addons, allowing customization of use cases, while the dependence on code generation is reduced, with support now limited to use cases only, which highly reduces build_runner
times.
Removing all builders except appBuilder
, and dependencies like provider, go_router, flutter_bloc, and freezed; Widgetbook 3 offers a cleaner, more user-friendly environment.
In this transition, familiar tools have been renamed or removed. For instance, the @WidgetbookUseCase
has evolved into @UseCase
, and @WidgetbookApp
is replaced with the @App
annotation.
Knobs have been redefined with intuitive naming conventions, and the previous categorization system has been replaced with a streamlined ‘directories’ approach.
If you use Widgetbook 2.4, check the migration guide to Widgetbook 3.
The Manual Approach vs. The Code Generation Approach
In the manual approach, you would individually catalog each widget and its variations in the Widgetbook. Although this approach provides excellent flexibility, it can become cumbersome and time-consuming, especially when dealing with many widgets or complex UIs.
On the other hand, the code generation approach introduced in Widgetbook 3 uses annotations to define widgets and their use cases. By running a code generator, you can automatically create a catalog of all annotated widgets in your codebase. This approach is far more efficient, especially for larger projects, as it saves time and reduces the risk of human error.
While both approaches have their merits, this guide will focus on the code generation approach due to its efficiency and productivity advantages.
Let’s begin to learn how Widgetbook 3 is supercharging your Flutter projects.
Installation
Kickstart your journey with Widgetbook 3 by adding the necessary dependencies to your pubspec.yaml
file:
dependencies:
widgetbook_annotation: ^3.0.0
dev_dependencies:
widgetbook: ^3.0.0
widgetbook_generator: ^3.0.0
build_runner:
Next, create the entry point for your Widgetbook application by adding a new widgetbook.dart
file in your lib
directory. The file namewidgetbook.dart
can be changed to anything that you like.
// widgetbook.dart
import 'package:flutter/material.dart';
import 'package:widgetbook/widgetbook.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
// Import the generated directories variable
import 'widgetbook.directories.g.dart';
void main() {
runApp(const WidgetbookApp());
}
// The @App annotation generates a file containing
// a single variable called directories.
@widgetbook.App()
class WidgetbookApp extends StatelessWidget {
const WidgetbookApp({super.key});
@override
Widget build(BuildContext context) {
return Widgetbook.material(
// Use the generated directories variable
directories: directories,
);
}
}
This is a basic implementation of Widgetbook in a Flutter project. The code imports necessary packages and generates a Widgetbook application.
The @App
annotation marks the WidgetbookApp
class which extends StatelessWidget. The WidgetbookApp’s build method then returns an Widgetbook.material
instance. The directories
variable, auto-generated by the @App
annotation, is passed into Widgetbook.material
as an argument, setting up the necessary directories for the Widgetbook app.
This implementation allows developers to preview their widgets in a catalog-like structure, promoting easy accessibility and reusability of components.
Directories are a list of folders, components, and use cases. Generator creates the directories variable automatically.
Remember that you can create a Widgetbook entry point in your existing app or create a new independent one.
// single app
flutter_app
└─── lib
| └─── feature.dart
│ └─── main.dart
│ └─── main.widgetbook.dart
└─── pubspec.yaml
// separate app
flutter_app
└─── feature_1
└─── app
| └───lib
| | └─── main.dart
| └─── pubspec.yaml
└─── widgetbook_app
| └─── lib
| | └─── main.widgetbook.dart
| └─── pubspec.yaml
Component and Use Case
In Figma, components are reusable UI elements that can have multiple variants. For consistency, in Widgetbook, Widgets are referred to as components.
Widgetbook follows a component and use-case approach, with a single component having one or multiple use cases. A use-case can be a variant of a component or represent a specific component state.
Developers use a folder tree structure to organize and catalog components in Widgetbook.
Annotating Widgets
Widgetbook 3's power lies in its ability to generate use cases through annotations. Let’s take an example of a CustomButton
widget:
// lib/components/custom_button.dart
import 'package:flutter/material.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
class CustomButton extends StatelessWidget {
const CustomButton({
super.key,
required this.title,
this.onPressed,
});
final String title;
final VoidCallback? onPressed;
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
child: Text(title),
);
}
}
@widgetbook.UseCase(
name: 'Enabled',
type: CustomButton,
)
CustomButton enabledButton(BuildContext context) {
return CustomButton(
title: 'Enabled',
onPressed: () {},
);
}
@widgetbook.UseCase(
name: 'Disabled',
type: CustomButton,
)
CustomButton disabledButton(BuildContext context) {
return const CustomButton(
title: 'Disabled',
);
}
The @widgetbook.UseCase
annotation in Widgetbook defines a specific use-case scenario for a particular widget. It acts as a marker for the Widgetbook tool to generate a code snippet showing the specified widget in the designated scenario.
In the context of @widgetbook.UseCase(name: 'Enabled', type: CustomButton,)
:
name
: This parameter represents the title or description of the use case scenario. It allows users to quickly identify and understand the context in which the widget is being used. In this example, 'Enabled
' suggests that this use case will demonstrate theCustomButton
widget in its enabled state.type
: This parameter signifies the type of widget that the use-case is associated with. It refers to the widget class you demonstrate under the specified use case. In this example,CustomButton
the widget is showcased under the 'Enabled
' use case.
Running the Code Generator
Once you have set up the annotations for your widgets, you can run the code generator:
flutter pub run build_runner build --delete-conflicting-outputs
You’ll see widgetbook.directories.g.dart
file is now generated right next to the entry point file widgetbook.dart
that you have created.
// 'widgetbook.directories.g.dart'
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_import, prefer_relative_imports, directives_ordering
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// AppGenerator
// **************************************************************************
import 'package:full_example/blog.dart';
import 'package:full_example/components/container.dart';
import 'package:full_example/components/custom_card.dart';
import 'package:full_example/components/custom_text_field.dart';
import 'package:full_example/customs/custom_knob.dart';
import 'package:widgetbook/widgetbook.dart';
final directories = [
WidgetbookComponent(
name: 'CustomButton',
useCases: [
WidgetbookUseCase(
name: 'Enabled',
builder: (context) => enabledButton(context),
),
WidgetbookUseCase(
name: 'Disabled',
builder: (context) => disabledButton(context),
),
],
),
];
If you follow a manual approach, you will write this file all on your own. As you can see, the generator approach simplifies the process a lot.
Note that this file is imported to the entry point `widgetbook.dart`
Running Widgetbook
Widgetbook supports Flutter Desktop and Web. To start exploring your widgets in Widgetbook, run the following command:
flutter run -d chrome -t lib/widgetbook.dart
You can change chrome
with your Desktop of choice, macos
, linux
or windows
. Next, you’ll be able to see your catalogs.
Addons
Addons in Widgetbook offer a flexible way to customize your components' presentation. You can use them to apply themes, adjust text scales, select different locales, and emulate different device frames, among other functionalities.
Let’s consider a simplified example where you will use the MaterialThemeAddon
and DeviceFrameAddon
in your Widgetbook:
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:widgetbook/widgetbook.dart';
void main() {
runApp(const WidgetbookApp());
}
class WidgetbookApp extends StatelessWidget {
const WidgetbookApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Widgetbook.material(
directories: [], // Add your directories here
addons: [
MaterialThemeAddon(
themes: [
WidgetbookTheme(
name: 'Light',
data: ThemeData.light(),
),
WidgetbookTheme(
name: 'Dark',
data: ThemeData.dark(),
),
],
),
DeviceFrameAddon(
devices: [
Devices.ios.iPhoneSE,
Devices.ios.iPhone13,
],
),
],
);
}
}
In this example, we’ve used the MaterialThemeAddon
to add two themes, light and dark, to your Widgetbook.
The DeviceFrameAddon
lets us preview your widgets on different device frames like the iPhone SE and iPhone 13.
You can see how device selection works below in action:
This way, addons help you tweak your components’ presentation in the Widgetbook interface, enhancing your widget testing and previewing experience.
Now let’s take a look at build-in Addons such as MaterialThemeAddon
, TextScaleAddon
, LocalizationAddon
, and DeviceFrameAddon
in Widgetbook.
// lib/widgetbook.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:widgetbook/widgetbook.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
import 'package:widgetbook_core/widgetbook_core.dart';
import 'widgetbook.g.dart';
void main() {
runApp(const WidgetbookApp());
}
@widgetbook.App()
class WidgetbookApp extends StatelessWidget {
const WidgetbookApp({super.key});
@override
Widget build(BuildContext context) {
return Widgetbook.material(
directories: directories,
addons: [
MaterialThemeAddon(
themes: [
WidgetbookTheme(
name: 'Light',
data: yourCustomLightTheme
),
WidgetbookTheme(
name: 'Dark',
data: yourCustomTheme
),
],
),
TextScaleAddon(
scales: [1.0, 2.0],
),
LocalizationAddon(
locales: [
const Locale('en', 'US'),
],
localizationsDelegates: [
DefaultWidgetsLocalizations.delegate,
DefaultMaterialLocalizations.delegate,
],
),
DeviceFrameAddon(
devices: [
Devices.ios.iPhoneSE,
Devices.ios.iPhone13,
],
),
],
);
}
}
The provided example showcases the diverse application of Addons in Widgetbook. It includes the utilization of MaterialThemeAddon, TextScaleAddon, LocalizationAddon, and DeviceFrameAddon. While you have these Addons, you can still create your custom Addons and integrate them into your widget tree. Learn more about Addons on the documentation website.
The Widgetbook’s Addon system is structured to boost flexibility, ensure smooth interoperability, and enrich the overall user experience. Developers can discover new avenues and enhance their development process efficiency by mastering and adeptly applying this system.
Knobs
Knobs are interactive tools that allow you to dynamically adjust the properties of your widget directly from the Widgetbook UI. They facilitate understanding how the widget behaves under different conditions, which can aid developers and designers in making robust and well-rounded component designs.
Now, let’s look at an example of a custom button with two states: enabled and disabled. Let’s add a new use case to use Knobs.
In the enabled state, the button is clickable, and in the disabled state, it’s not. We’ll utilize knobs to change the title of the button and its state.
First, Let’s review CustomButton
widget that you have created already:
import 'package:flutter/material.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
class CustomButton extends StatelessWidget {
const CustomButton({
Key? key,
required this.title,
this.onPressed,
}) : super(key: key); final String title;
final VoidCallback? onPressed; @override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
child: Text(title),
);
}
}
Now, let’s explain the Widgetbook use case with knobs for the CustomButton
Widget:
@widgetbook.UseCase(
name: 'Customizable',
type: CustomButton,
)
CustomButton customizableButton(BuildContext context) {
return CustomButton(
title: context.knobs.string(label: 'Title', initialValue: 'Click Me'),
onPressed: context.knobs.boolean(label: 'Enabled', initialValue: true)
? () {}
: null,
);
}
In the above use case, we’ve added two knobs:
title
knob: This knob of typestring
allows you to dynamically change the text on the button from the Widgetbook UI. The initial value is set as 'Click Me'.enabled
knob: This knob of typeboolean
toggles the state of the button between enabled (clickable) and disabled (not clickable).
This way, knobs provide a quick and easy way to test and document different states and behaviors of a widget.
Make sure to run build_runner
and also run
the project.
Widgetbook Cloud
Widgetbook Cloud takes the utility of Widgetbook to a new level by facilitating seamless collaboration among your entire product team.
While Widgetbook assists Flutter developers in efficiently creating, testing, and cataloging widgets, Widgetbook Cloud extends this functionality, simplifying the review and feedback process.
It enables sharing widgets with designers, project managers, clients, and other stakeholders, streamlining communication and fostering more cohesive and efficient product development.
Key Features of Widgetbook Cloud:
- Share Widgets Globally: The platform allows developers to share widgets with stakeholders effortlessly. It means designers, project owners, and clients can review the widgets without setting up the development environment.
- Streamlined Reviews: Widgetbook Cloud simplifies the review process by providing a guided approach. Stakeholders can review and test widgets in all possible configurations and provide precise feedback, eliminating the need for multiple Slack messages, screenshots, emails, or meetings.
- Figma Integration: Widgetbook Cloud boasts a seamless Figma integration feature. This means developers can easily link their widgets to the corresponding design components in Figma. It ensures design consistency and improves collaboration between developers and designers.
- Version Comparison: This feature allows stakeholders to compare different versions of a widget side by side. They can see what changes have been made from the previous implementation, making it easier to track the development progress.
Today, you can get early access to Widgetbook Cloud. Check out the website for more information.
Conclusion
In conclusion, Widgetbook 3 is an open-source package for Flutter developers, enhancing the widget development process, testing, and documentation. With catalogs, developers can organize components efficiently.
Use cases facilitate easy testing and sharing of different widget states, while addons enable customization of the development environment. The software’s adaptability to various themes, text scales, locales, and device frames offers an integrated and robust testing interface.
Widgetbook Cloud revolutionizes how teams collaborate, providing a unified platform to review, discuss, and enhance Flutter widgets. Whether you’re a developer looking for precise feedback or a designer striving for pixel-perfect implementation, Widgetbook Cloud covers you.
You can explore the available addons, knobs, and Widgetbook Cloud and how to use them in the Widgetbook documentation. Remember that you can join the Widgetbook Discord server to discuss, learn more and talk to us to receive support.