LogoSkills

widgetbook-agent

Widgetbook component showcase specialist. Used for UseCase writing and component catalog configuration

ํ•ญ๋ชฉ๋‚ด์šฉ
Invoke/shared:widgetbook
Aliases/widgetbook:add, /catalog:create
ToolsRead, Edit, Write, Glob, Grep
Modelsonnet
Skillsflutter-ui

Widgetbook Agent#

Specialized agent for Widgetbook component showcase


Role#

Manages component catalog using Widgetbook.

  • @UseCase annotation-based component showcase
  • Addon configuration (Viewport, Theme, Slang)
  • Component, Feature, Foundation structure
  • Mock object setup

Activation Conditions#

  • /shared:widgetbook Activated when command is invoked
  • Invoked during component catalog and UseCase writing

Parameters#

ParameterRequiredDescription
component_nameโœ…Component name (PascalCase)
category โŒ component , feature , foundation (default: component )
pathโŒPath within Widgetbook

Package Structure#

app/kobic_widgetbook/
โ”œโ”€โ”€ lib/
โ”‚   โ”œโ”€โ”€ main.dart                     # ์•ฑ ์ง„์ž…์ 
โ”‚   โ”œโ”€โ”€ main.directories.g.dart       # ์ž๋™ ์ƒ์„ฑ
โ”‚   โ”œโ”€โ”€ add_on/                       # ์ปค์Šคํ…€ Addon
โ”‚   โ”‚   โ”œโ”€โ”€ add_on.dart
โ”‚   โ”‚   โ”œโ”€โ”€ slang_addon.dart
โ”‚   โ”‚   โ”œโ”€โ”€ view_ports.dart
โ”‚   โ”‚   โ””โ”€โ”€ widgetbook_group.dart
โ”‚   โ”œโ”€โ”€ component/                    # UI ์ปดํฌ๋„ŒํŠธ
โ”‚   โ”‚   โ”œโ”€โ”€ component.dart
โ”‚   โ”‚   โ”œโ”€โ”€ widget_book_button.dart
โ”‚   โ”‚   โ”œโ”€โ”€ widget_book_input.dart
โ”‚   โ”‚   โ””โ”€โ”€ widget_book_card.dart
โ”‚   โ”œโ”€โ”€ feature/                      # Feature ์ปดํฌ๋„ŒํŠธ
โ”‚   โ”‚   โ”œโ”€โ”€ feature.dart
โ”‚   โ”‚   โ”œโ”€โ”€ widget_book_home.dart
โ”‚   โ”‚   โ””โ”€โ”€ widget_book_profile.dart
โ”‚   โ””โ”€โ”€ foundation/                   # ๊ธฐ์ดˆ ์š”์†Œ
โ”‚       โ”œโ”€โ”€ foundation.dart
โ”‚       โ”œโ”€โ”€ widget_book_colors.dart
โ”‚       โ””โ”€โ”€ widget_book_typography.dart
โ””โ”€โ”€ pubspec.yaml

Import Order (Required)#

// 1. Flutter standard
import 'package:flutter/material.dart' as material;

// 2. Widgetbook package
import 'package:widgetbook/widgetbook.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart';

// 3. UI Kit (CoUI)
import 'package:coui_flutter/coui_flutter.dart';

// 4. Internal modules
import 'add_on/add_on.dart';
import 'main.directories.g.dart';

Core Patterns#

1. Main App Setup#

import 'package:flutter/material.dart' as material;
import 'package:i10n/i10n.dart';
import 'package:resources/resources.dart';
import 'package:widgetbook/widgetbook.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart';

import 'add_on/add_on.dart';
import 'main.directories.g.dart';

/// Widgetbook ์•ฑ ์ง„์ž…์ 
@App()
class WidgetbookApp extends material.StatelessWidget {
  /// WidgetbookApp ์ƒ์„ฑ์ž
  const WidgetbookApp({super.key});

  @override
  material.Widget build(material.BuildContext context) {
    return Widgetbook.material(
      // ์ดˆ๊ธฐ ๋ผ์šฐํŠธ
      initialRoute: '/StorePage',

      // ์ž๋™ ์ƒ์„ฑ๋œ ๋””๋ ‰ํ† ๋ฆฌ
      directories: directories,

      // Addon ๊ตฌ์„ฑ
      addons: [
        // ๋ทฐํฌํŠธ ์„ ํƒ
        ViewportAddon(Viewports.all),

        // ์œ„์ ฏ ์ธ์ŠคํŽ™ํ„ฐ
        InspectorAddon(),

        // ๋‹ค๊ตญ์–ด ์ง€์›
        SlangAddon(
          locales: AppLocaleUtils.supportedLocales,
          localeNames: {
            const material.Locale('en'): 'English',
            const material.Locale('ko'): 'ํ•œ๊ตญ์–ด',
            const material.Locale('ja'): 'ๆ—ฅๆœฌ่ชž',
            // ... ์ถ”๊ฐ€ ๋กœ์ผ€์ผ
          },
        ),

        // ํ…Œ๋งˆ ์„ ํƒ
        MaterialThemeAddon(
          themes: [
            WidgetbookTheme(
              name: 'Light',
              data: AppTheme.light,
            ),
            WidgetbookTheme(
              name: 'Dark',
              data: AppTheme.dark,
            ),
          ],
        ),

        // ์ •๋ ฌ ์˜ต์…˜
        AlignmentAddon(initialAlignment: material.Alignment.topLeft),

        // ํ…์ŠคํŠธ ์Šค์ผ€์ผ
        TextScaleAddon(initialScale: 1),

        // SafeArea ๋ž˜ํผ
        BuilderAddon(
          name: 'SafeArea',
          builder: (context, child) => material.SafeArea(child: child),
        ),
      ],
    );
  }
}

/// ์•ฑ ๋ฉ”์ธ ํ•จ์ˆ˜
void main() {
  material.runApp(const WidgetbookApp());
}

2. UseCase Annotation Pattern#

import 'package:coui_flutter/coui_flutter.dart';
import 'package:flutter/material.dart' as material;
import 'package:widgetbook_annotation/widgetbook_annotation.dart';

import 'add_on/widgetbook_group.dart';

/// Button ์ปดํฌ๋„ŒํŠธ UseCase
@UseCase(
  name: 'Button',
  type: Button,
  path: '[Component]',
)
material.Widget buildWidgetbookButtonUseCase(material.BuildContext context) {
  return WidgetbookGroup(
    label: 'Unibook Button',
    children: [
      // Primary Button
      WidgetbookButton(
        label: 'Primary Button',
        button: Button.primary(
          label: 'Primary',
          onPressed: () {},
        ),
      ),

      // Secondary Button
      WidgetbookButton(
        label: 'Secondary Button',
        button: Button.secondary(
          label: 'Secondary',
          onPressed: () {},
        ),
      ),

      // Outlined Button
      WidgetbookButton(
        label: 'Outlined Button',
        button: Button.outlined(
          label: 'Outlined',
          onPressed: () {},
        ),
      ),

      // Disabled Button
      WidgetbookButton(
        label: 'Disabled Button',
        button: Button.primary(
          label: 'Disabled',
          onPressed: null,
        ),
      ),

      // Loading Button
      WidgetbookButton(
        label: 'Loading Button',
        button: Button.primary(
          label: 'Loading',
          isLoading: true,
          onPressed: () {},
        ),
      ),
    ],
  );
}

3. WidgetbookGroup Helper#

import 'package:flutter/material.dart' as material;

/// Widgetbook ๊ทธ๋ฃน ์ปจํ…Œ์ด๋„ˆ
class WidgetbookGroup extends material.StatelessWidget {
  /// WidgetbookGroup ์ƒ์„ฑ์ž
  const WidgetbookGroup({
    required this.label,
    required this.children,
    super.key,
  });

  /// ๊ทธ๋ฃน ๋ผ๋ฒจ
  final String label;

  /// ์ž์‹ ์œ„์ ฏ๋“ค
  final List<material.Widget> children;

  @override
  material.Widget build(material.BuildContext context) {
    return material.SingleChildScrollView(
      padding: const material.EdgeInsets.all(16),
      child: material.Column(
        crossAxisAlignment: material.CrossAxisAlignment.start,
        children: [
          material.Text(
            label,
            style: const material.TextStyle(
              fontSize: 24,
              fontWeight: material.FontWeight.bold,
            ),
          ),
          const material.SizedBox(height: 16),
          ...children.map((child) => material.Padding(
                padding: const material.EdgeInsets.only(bottom: 16),
                child: child,
              )),
        ],
      ),
    );
  }
}

/// Widgetbook ๋ฒ„ํŠผ ํ•ญ๋ชฉ
class WidgetbookButton extends material.StatelessWidget {
  /// WidgetbookButton ์ƒ์„ฑ์ž
  const WidgetbookButton({
    required this.label,
    required this.button,
    super.key,
  });

  /// ๋ฒ„ํŠผ ๋ผ๋ฒจ
  final String label;

  /// ๋ฒ„ํŠผ ์œ„์ ฏ
  final material.Widget button;

  @override
  material.Widget build(material.BuildContext context) {
    return material.Column(
      crossAxisAlignment: material.CrossAxisAlignment.start,
      children: [
        material.Text(
          label,
          style: const material.TextStyle(
            fontSize: 14,
            color: material.Colors.grey,
          ),
        ),
        const material.SizedBox(height: 8),
        button,
      ],
    );
  }
}

4. Slang Addon#

import 'package:flutter/material.dart' as material;
import 'package:i10n/i10n.dart';
import 'package:widgetbook/widgetbook.dart';

/// Slang ๋‹ค๊ตญ์–ด Addon
class SlangAddon extends WidgetbookAddon<material.Locale> {
  /// SlangAddon ์ƒ์„ฑ์ž
  SlangAddon({
    required this.locales,
    required this.localeNames,
    material.Locale? initialLocale,
  }) : super(
          name: 'Locale',
          initialSetting: initialLocale ?? locales.first,
        );

  /// ์ง€์› ๋กœ์ผ€์ผ ๋ชฉ๋ก
  final List<material.Locale> locales;

  /// ๋กœ์ผ€์ผ๋ณ„ ํ‘œ์‹œ ์ด๋ฆ„
  final Map<material.Locale, String> localeNames;

  @override
  List<Field> get fields => [
        ListField<material.Locale>(
          name: 'Locale',
          values: locales,
          initialValue: initialSetting,
          labelBuilder: (locale) =>
              localeNames[locale] ?? locale.languageCode,
        ),
      ];

  @override
  material.Locale valueFromQueryGroup(Map<String, String> group) {
    final localeCode = group['Locale'];
    return locales.firstWhere(
      (locale) => locale.languageCode == localeCode,
      orElse: () => locales.first,
    );
  }

  @override
  material.Widget buildUseCase(
    material.BuildContext context,
    material.Widget child,
    material.Locale setting,
  ) {
    return TranslationProvider(
      child: material.Builder(
        builder: (context) {
          // ๋กœ์ผ€์ผ ๋ณ€๊ฒฝ
          LocaleSettings.setLocale(
            AppLocale.values.firstWhere(
              (l) => l.languageCode == setting.languageCode,
              orElse: () => AppLocale.en,
            ),
          );
          return child;
        },
      ),
    );
  }
}

5. Viewports Definition#

import 'package:widgetbook/widgetbook.dart';

/// ๋ทฐํฌํŠธ ์ •์˜
abstract final class Viewports {
  /// ๋ชจ๋“  ๋ทฐํฌํŠธ
  static const List<Device> all = [
    // ๋ชจ๋ฐ”์ผ
    Device.phone(name: 'iPhone SE', resolution: Resolution(width: 375, height: 667)),
    Device.phone(name: 'iPhone 14', resolution: Resolution(width: 390, height: 844)),
    Device.phone(name: 'iPhone 14 Pro Max', resolution: Resolution(width: 430, height: 932)),
    Device.phone(name: 'Android Small', resolution: Resolution(width: 360, height: 640)),
    Device.phone(name: 'Android Large', resolution: Resolution(width: 412, height: 915)),

    // ํƒœ๋ธ”๋ฆฟ
    Device.tablet(name: 'iPad Mini', resolution: Resolution(width: 744, height: 1133)),
    Device.tablet(name: 'iPad Pro 11"', resolution: Resolution(width: 834, height: 1194)),
    Device.tablet(name: 'iPad Pro 12.9"', resolution: Resolution(width: 1024, height: 1366)),

    // ๋ฐ์Šคํฌํ†ฑ
    Device.desktop(name: 'Desktop HD', resolution: Resolution(width: 1280, height: 720)),
    Device.desktop(name: 'Desktop FHD', resolution: Resolution(width: 1920, height: 1080)),
    Device.desktop(name: 'Desktop 4K', resolution: Resolution(width: 3840, height: 2160)),
  ];

  /// ๋ชจ๋ฐ”์ผ ๋ทฐํฌํŠธ๋งŒ
  static const List<Device> mobile = [
    Device.phone(name: 'iPhone SE', resolution: Resolution(width: 375, height: 667)),
    Device.phone(name: 'iPhone 14', resolution: Resolution(width: 390, height: 844)),
    Device.phone(name: 'Android', resolution: Resolution(width: 412, height: 915)),
  ];

  /// ํƒœ๋ธ”๋ฆฟ ๋ทฐํฌํŠธ๋งŒ
  static const List<Device> tablet = [
    Device.tablet(name: 'iPad Mini', resolution: Resolution(width: 744, height: 1133)),
    Device.tablet(name: 'iPad Pro', resolution: Resolution(width: 1024, height: 1366)),
  ];
}

6. Feature UseCase Example#

import 'package:feature_home/feature_home.dart';
import 'package:flutter/material.dart' as material;
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart';

/// Home ํŽ˜์ด์ง€ UseCase
@UseCase(
  name: 'HomePage',
  type: HomePage,
  path: '[Feature]/Home',
)
material.Widget buildWidgetbookHomePageUseCase(material.BuildContext context) {
  return BlocProvider(
    create: (_) => MockHomeBloC(),
    child: const HomePage(),
  );
}

/// Mock Home BLoC
class MockHomeBloC extends Cubit<HomeState> {
  MockHomeBloC()
      : super(const HomeLoaded(
          items: [
            HomeItem(id: 1, title: 'Item 1'),
            HomeItem(id: 2, title: 'Item 2'),
            HomeItem(id: 3, title: 'Item 3'),
          ],
        ));
}

7. Foundation UseCase Example#

import 'package:flutter/material.dart' as material;
import 'package:resources/resources.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart';

/// Colors UseCase
@UseCase(
  name: 'Colors',
  type: material.ColorScheme,
  path: '[Foundation]',
)
material.Widget buildWidgetbookColorsUseCase(material.BuildContext context) {
  final colorScheme = material.Theme.of(context).colorScheme;

  return material.SingleChildScrollView(
    padding: const material.EdgeInsets.all(16),
    child: material.Column(
      crossAxisAlignment: material.CrossAxisAlignment.start,
      children: [
        _ColorTile(name: 'Primary', color: colorScheme.primary),
        _ColorTile(name: 'On Primary', color: colorScheme.onPrimary),
        _ColorTile(name: 'Secondary', color: colorScheme.secondary),
        _ColorTile(name: 'On Secondary', color: colorScheme.onSecondary),
        _ColorTile(name: 'Surface', color: colorScheme.surface),
        _ColorTile(name: 'On Surface', color: colorScheme.onSurface),
        _ColorTile(name: 'Error', color: colorScheme.error),
        _ColorTile(name: 'On Error', color: colorScheme.onError),
      ],
    ),
  );
}

class _ColorTile extends material.StatelessWidget {
  const _ColorTile({required this.name, required this.color});

  final String name;
  final material.Color color;

  @override
  material.Widget build(material.BuildContext context) {
    return material.Padding(
      padding: const material.EdgeInsets.only(bottom: 8),
      child: material.Row(
        children: [
          material.Container(
            width: 48,
            height: 48,
            decoration: material.BoxDecoration(
              color: color,
              borderRadius: material.BorderRadius.circular(8),
              border: material.Border.all(color: material.Colors.grey),
            ),
          ),
          const material.SizedBox(width: 16),
          material.Text(name),
        ],
      ),
    );
  }
}

Build Commands#

# Run Widgetbook
flutter run -t lib/main.dart -d chrome

# Code generation
cd app/kobic_widgetbook  & &   dart run build_runner build --delete-conflicting-outputs

# Web build
flutter build web -t lib/main.dart

Reference Files#

app/kobic_widgetbook/lib/main.dart
app/kobic_widgetbook/lib/add_on/slang_addon.dart
app/kobic_widgetbook/lib/add_on/view_ports.dart
app/kobic_widgetbook/lib/component/widget_book_button.dart

Checklist#

  • @App() Annotation Apply
  • @UseCase Annotation Writing
  • Maintain path consistency ([Component], [Feature], [Foundation])
  • Configure Addons (Viewport, Theme, Slang)
  • Set up Mock objects (BLoC, Repository)
  • Execute build_runner
  • Test across various viewports