widgetbook 2.0.8-beta widgetbook: ^2.0.8-beta copied to clipboard
A flutter storybook that helps professionals and teams to catalogue their widgets.
A flutter package which helps developers cataloguing their widgets, testing them quickly on multiple devices and themes, and sharing them easily with designers and clients. Inspired by Storybook.js and flutterbook.
See it in action! #
Check out the Widgetbook
with the example app on our homepage. Furthermore, you can check out the code of the app at github.
Other packages #
package:widgetbook can be used with package:widgetbook_annotation and package:widgetbook_generator to make setting up and maintaining Widgetbook easier. Check out the other packages:
Package | Pub |
---|---|
package:widgetbook | |
package:widgetbook_annotation | |
package:widgetbook_generator |
Usage #
import 'package:flutter/material.dart';
import 'package:widgetbook/widgetbook.dart';
void main() {
runApp(const HotreloadWidgetbook());
}
class HotreloadWidgetbook extends StatelessWidget {
const HotreloadWidgetbook({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Widgetbook.material(
categories: [
WidgetbookCategory(
name: 'widgets',
widgets: [
WidgetbookComponent(
name: 'Button',
useCases: [
WidgetbookUseCase(
name: 'elevated',
builder: (context) => ElevatedButton(
onPressed: () {},
child: const Text('Widgetbook'),
),
),
],
),
],
)
],
themes: [
WidgetbookTheme(
name: 'Light',
data: ThemeData.light(),
),
WidgetbookTheme(
name: 'Dark',
data: ThemeData.dark(),
),
],
appInfo: AppInfo(name: 'Example'),
);
}
}
Getting Started #
This package provides a flutter widget called Widgetbook
in which custom widgets from your app can be injected.
Setting up #
First, add the dependency to your pubspec.yaml
file:
# pubspec.yaml
dev_dependencies:
widgetbook:
Since the Widgetbook is launched as a separate app, it is recommended to create another main
method. This enables you to switch between your app and Widgetbook
at any time. You can even launch your app and Widgetbook
simultaneously.
The folder structure might look like this:
example_app
├─ lib
│ ├─ main.dart
├─ widgetbook
│ ├─ main.dart
│ ├─ widgetbook.dart
├─ pubspec.yaml
The widgetbook/widgetbook.dart
file contains the Widgetbook
wrapped within a stateless widget that enables hot reloading. The code looks like this:
class HotReload extends StatelessWidget {
const HotReload({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Widgetbook(...);
}
}
In the widgetbook/main.dart
run the HotReload
widget:
void main() {
runApp(HotReload());
}
Run the Widgetbook #
Widgetbook
is supported on the following environments.
Environment | Status | Comment |
---|---|---|
MacOS | ✅ | |
Windows | ✅ | |
Linux | ✅ | |
Web | ✅ | No hot reload, but hot restart (see Issue 4) |
Mobile | ➖ | Will run, but is not optimized. If you see a usecase for Widgetbook on mobile let us know. |
See the Desktop support for Flutter page for setup instructions.
Run the Widgetbook
main method by executing flutter run -t widgetbook/main.dart
.
NOTE: If you are using package:widgetbook_generator see the documentation on how to run Widgetbook.
Constructors #
Widgetbook
allows developers to define whatever Theme
they have defined for their app. To accompany every theme and the ones defined by Flutter, the following constructors exist:
Theme | Constructor | Defaults to |
---|---|---|
Custom Theme | Widgetbook<YourTheme> |
➖ |
Material | Widgetbook.material |
Widgetbook<ThemeData> |
Cupertino | Widgetbook.cupertino |
Widgetbook<CupertinoThemeData> |
As you can see from the constructor definitions, Widgetbook
allows to define your Theme type to accompany any implementation. However, most developers will likely use Widgetbook.material
as shown in the example above.
Properties #
Widgetbook
defines various properties to customize how your Widget
s will be rendered.
categories
#
Your widgets can be catalogued by using different Organizer
s. The available organizers are: WidgetbookCategory
, WidgetbookFolder
, WidgetbookComponent
and WidgetbookUseCase
.
Both WidgetbookCategory
and WidgetbookFolder
can contain sub folders and WidgetbookComponent
elements. However, WidgetbookComponent
can only contain WidgetbookUseCase
s.
class HotReload extends StatelessWidget {
const HotReload({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Widgetbook(
categories: [
WidgetbookCategory(
name: 'widgets',
widgets: [
WidgetbookComponent(
name: '$CustomWidget',
useCases: [
WidgetbookUseCase(
name: 'Default',
builder: (context) => CustomWidget(),
),
],
),
],
folders: [
WidgetbookFolder(
name: 'Texts',
widgets: [
WidgetbookComponent(
name: 'Normal Text',
useCases: [
WidgetbookUseCase(
name: 'Default',
builder: (context) => Text(
'The brown fox ...',
),
),
],
),
],
),
],
),
],
appInfo: AppInfo(
name: 'Widgetbook Example',
),
);
}
}
appInfo
#
The appInfo
property allows users to label the Widgetbook
in case you are maintaining more than one Widgetbook
for multiple projects. Customize Widgetbook
's name according to the project by using appInfo:
Widgetbook.material(
appInfo: AppInfo(
name: 'Your apps name',
),
)
Localization #
Widgetbook defines the two properties supportedLocales
and localizationsDelegates
to support localization of Widget
s. These values behave as described in Flutter Internationalization.
Widgetbook.material(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
Locale('en'), // English, no country code
Locale('es'), // Spanish, no country code
],
)
themes
#
Import your app's theme for a realistic preview by using Widgetbook
's theme
property:
Widgetbook.material(
themes: [
WidgetbookTheme(
name: 'Light',
data: ThemeData.light(),
),
WidgetbookTheme(
name: 'Dark',
data: ThemeData.dark(),
),
],
)
devices
#
Customize the preview by defining preview devices:
Widgetbook.material(
devices: [
Apple.iPhone11,
Samsung.s21ultra,
]
)
Right now, there is a predefined list of devices. If you need more devices, you can either add them on your own or let us know which ones you need in our Discord.
Define your own device #
You can also define your own device by using the Device
class:
Device(
name: 'Custom Device',
resolution: Resolution.dimensions(
width: 500,
height: 500,
scaleFactor: 2,
),
type: DeviceType.tablet,
),
frames
#
The frames
property allows developers to define different ways of how the frame of a device is visualized. The following WidgetbookFrame
s are defined:
WidgetbookFrame |
Comment | Is default |
---|---|---|
WidgetbookFrame.defaultFrame |
The default frame of Widgetbook |
✅ |
WidgetbookFrame.noFrame |
No frame - this just shows the use case without any device restrictions | ✅ |
WidgetbookFrame.deviceFrame |
A frame known from the device_frame package | ✅ |
If the Device Frame option is active, the Widgetbook devices will be mapped to the devices of device_frame.
textScaleFactors
#
The textScaleFactors
property allows you to define a list of different text scales which are injected (and can then be accessed) via the MediaQuery
. The list defaults to textScaleFactors
of [ 1.0 ]
.
Builders #
Widgetbook
exposes various builder functions to allow customization of how WidgetbookUseCase
s are displayed.
deviceFrameBuilder
#
The deviceFrameBuilder
in combination with the frames
property can be used to add your or an existing implementation of a device frame:
For the device_frame package this can look like this:
Widgetbook.material(
deviceFrameBuilder: (context, device, renderMode, child) {
if (renderMode == DeviceFrame.deviceFrame()) {
return frame.DeviceFrame(
device: frame.Devices.ios.iPhone12,
screen: child,
);
}
// default to no device frame
return child;
},
)
localizationBuilder
#
The default of localizationBuilder
is defined as:
(
BuildContext context,
List<Locale> supportedLocales,
List<LocalizationsDelegate<dynamic>>? localizationsDelegates,
Locale activeLocale,
Widget child,
) {
if (localizationsDelegates != null) {
return Localizations(
locale: activeLocale,
delegates: localizationsDelegates,
child: child,
);
}
return child;
};
themeBuilder
#
The themeBuilder
allows you to inject theme data into the Widget
tree. An implementation for CupertinoThemeData
could look like this:
Widgetbook<CupertinoThemeData>(
themeBuilder: (
BuildContext context,
CupertinoThemeData theme,
Widget child,
) {
return CupertinoTheme(
data: theme,
child: child,
);
},
)
scaffoldBuilder
and useCaseBuilder
#
Both the scaffoldBuilder
and useCaseBuilder
can be used to wrap the Widget
with e.g. a Scaffold
or some other Widget
like a Center
, Container
or Padding
.
Supported Flutter version #
We are currently aiming to support the following flutter version
flutter: >=2.8.0
Using Widgetbook for a package #
A lot of app projects implement dedicated packages for UI components. These packages often do not define a (desktop) app. Therefore, it's hard to use Widgetbook for these packages. This section will explain how you can get Widgetbook running in such a scenario.
Let's assume you have set up a monorepo with the following folder structure with two packages ui_components
and core_api
:
monorepo
├─ docs
├─ packages
│ ├─ ui_components
│ ├─ core_api
To create a widgetbook for the ui_components
package, we recommend to create a new folder named widgetbooks
in the root of the repository and add a flutter (desktop) app named ui_components_widgetbook
. The folder structure of your repository will look like this:
monorepo
├─ docs
├─ packages
│ ├─ ui_components
│ ├─ core_api
├─ widgetbooks
│ ├─ ui_components_widgetbook
│ │ ├─ lib
│ │ ├─ macos
│ │ ├─ pubspec.yaml
Make sure to modify widgetbooks/ui_components_widgetbook/pubspec.yaml
to depend on ui_components
:
pubspec.yaml:
dependencies:
ui_components:
path: ../../packages/ui_components
You can now use all exported Widget
s from your library to compose your Widgetbook.
Known Issues #
- Hot reloading on web is currently not working properly. This is due to the fact that hot reloading is actually a restart. The problem is tracked in widgetbook/issues/4. For now we recommended to use MacOS or Windows as a platform for development.
Let us know how you feel about Widgetbook #
We are funded and aim to shape Widgetbook
to your (and your team's) needs. If you have questions, feature requests or issues let us know on Discord or GitHub or book a call with the founders via Calendly. We're looking forward to build a community and discuss your feedback on our channel! 💙