Install Widgetbook in a Flutter project (Single-App Setup)

Install Widgetbook in a Flutter project (Single-App Setup)

Public
Public
Tags
Flutter
Design System
Widgetbook
Developer Tools
Author
Ruby Chu
Series
Status
Done
Medium
dev.to

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.dart file in your lib folder
  • 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.dart file that has the directories variable:
    • $ dart run build_runner build -d
      This will generate something like lib/widgetbook.directories.g.dart

Step 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 CustomButton and 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.

Resources