How I Encountered This Issue
Once a project starts to grow, custom widgets grow with it: buttons, cards, form fields, empty states… and suddenly everyone on the team is asking:
- “What variants do we already have?”
- “How should this look in dark mode?”
- “Can I quickly preview this widget without running the whole app?”
I wanted a simple way to document and preview UI components in one place, like for front-end web development, and that’s how I found Widgetbook for mobile development.
Create UI documentation for your project
If you're not familiar with Widgetbook, you can explore the demo to get an idea.
Essentially, it's a UI component documentation tool, perfect for teams with lots of custom widgets. It can be incredibly useful!
How to setup Widgetbook in a project?
You can integrate Widgetbook into your project in two ways:
a) as a single app, or b) as a separate app.
Below is how the project folder structure would look after setting up Widgetbook using each approach.
// 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
Here we go, I'll walk you through how I set up Widgetbook in a single app form for my project.
Step 1: Install Widgetbook Package
- Add the packages to pubspec.yaml
dependencies: // ... your other dependencies widgetbook_annotation: ^3.2.0 widgetbook: ^3.9.0 dev_dependencies: build_runner: widgetbook_generator: ^3.9.0
- Run
flutter pub get
Step 2: Create a Widgetbook entry file
- Create a
widgetbook.dartfile in yourlibfolder
- Paste these code
// lib/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 file is basically your Widgetbook app entry point.
Step 3: Add a widget + define use cases
- Create another file to test, for example,
custom_button.dart
- Paste these code
// 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', ); }
Each
@UseCase becomes a “previewable state” inside Widgetbook.Step 4: Generate the directories file
- Run the following command to generate the
main.directories.g.dartfile that has thedirectoriesvariable:
$ dart run build_runner build -d
This will generate something like
lib/widgetbook.directories.g.dartStep 5: Run Widgetbook
- Run Widgetbook using your Widgetbook entry file
$ flutter run -d chrome -t lib/widgetbook.dart
- Now you should see your Widgetbook website, with your
CustomButtonand its use cases listed in the navigation.
Additional Notes
If you see generator conflicts, I usually re-run with the “delete conflicting outputs” option:
$ flutter pub run build_runner build --delete-conflicting-outputs
- For teams, the “single-app” approach is nice because:
- it lives next to your real widgets,
- it’s easy to keep updated,
- you don’t have to maintain a separate app repo/config.
- Once you scale up, you’ll probably want to organize your widget folders (and use cases) by feature/domain so the Widgetbook navigation stays clean.
Final Thoughts
Widgetbook ended up being one of those tools that feels “optional”… until your widget library grows. After I set it up, it became much easier to:
- check existing variants before building new UI,
- communicate UI intent (especially with juniors or cross-functional teammates),
- review components without booting the whole app.
However, I would only suggest using it if your project has a lot of custom widgets, or your team has a really large-scale or mature own design system, else it could be trouble-some to have another semi-project inside your app to maintain.
That said, I’d only recommend using it if your project has a large number of custom widgets, or if your team already maintains a large-scale, mature design system.
Otherwise, it can start to feel like maintaining a small project inside your app, which may be more overhead than it’s worth.
