📚

widgetbookを使ってみる

2024/07/23に公開

Webで言うところのStorybookみたいなものか

FlutterにもStorybookのようなものがあるみたいだ。
https://storybook.js.org/

https://www.widgetbook.io/
公式より引用
Widgetbookのオープンソースパッケージを使用すると、1つの中央ウィジェットライブラリまたはデザインシステムでウィジェットをカタログ化できます。Widgetbook Cloudは、プルリクエストのビジュアルバグを自動的にキャッチし、FlutterウィジェットをFigmaと同期します。


こちらのサイトを参考に進めていくことになるのですが、自分で作ったFlutterプロジェクトの中に、新しくプロジェクトを作るみたいです。

サンプルも作ってみました

https://docs.widgetbook.io/guides/quick-start

まずは新規プロジェクトを作っておきます。

flutter create widget_book_tutorial

プロジェクトを開いて、公式の手順を見ながら、ターミナルとyamlの設定をしていきます。

Bootstrap

  1. The first step to start using Widgetbook is to create a new separate Flutter app for your widget catalog.
## All platforms
flutter create widgetbook --empty

## Certain platforms
flutter create widgetbook --empty --platforms=web,macos

  1. Widgetbookのオープンソースパッケージを使用すると、1つの中央ウィジェットライブラリまたはデザインシステムでウィジェットをカタログ化できます。Widgetbook Cloudは、プルリクエストのビジュアルバグを自動的にキャッチし、FlutterウィジェットをFigmaと同期します。
- name: widgetbook
+ name: widgetbook_workspace

  1. 以下の依存関係をwidgetbookプロジェクトに追加してください:

プロジェクトの中に作ったwidgetbookに、ターミナルで移動して以下のコマンドを実行して、パッケージを追加します。

flutter pub add widgetbook widgetbook_annotation dev:widgetbook_generator dev:build_runner

アプリをwidgetbook/pubspec.yamlファイルにパス依存として追加します:

dependencies:
  your_app:
    path: ../

セットアップが終わると、フォルダ構造は次のようになるはずだ:

your_app/
├── pubsepc.yaml
├── lib/
├── ...
└── widgetbook/
    ├── pubsepc.yaml
    ├── lib/
    └── ...

そして、widgetbook/pubspec.yamlファイルは以下のようになるはずです:

name: widgetbook_workspace
# ...

dependencies:
  widgetbook_annotation: ^3.1.0
  widgetbook: ^3.8.1
  your_app:
    path: ../

dev_dependencies:
  build_runner:
  widgetbook_generator: ^3.8.0

Your First Use-case

このセクションでは、簡単なユースケースを作成し、Widgetbookアプリの中に表示します。

  1. カタログにしたいウィジェットをyour_appから選びます。この例では、CoolButtonウィジェットを使用します。

  2. widgetbookアプリ内に、widgetbook/lib/cool_button.dartファイルを作成します。

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

// Import the widget from your app
import 'package:your_app/cool_button.dart';

.UseCase(name: 'Default', type: CoolButton)
Widget buildCoolButtonUseCase(BuildContext context) {
  return CoolButton();
}
  1. widgetbookアプリのlib/main.dartにファイルを作成してください。
import 'package:flutter/material.dart';
import 'package:widgetbook/widgetbook.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;

// This file does not exist yet,
// it will be generated in the next step
import 'main.directories.g.dart';

void main() {
  runApp(const WidgetbookApp());
}

.App()
class WidgetbookApp extends StatelessWidget {
  const WidgetbookApp({super.key});

  
  Widget build(BuildContext context) {
    return Widgetbook.material(
      // The [directories] variable does not exist yet,
      // it will be generated in the next step
      directories: directories,
    );
  }
}
  1. 以下のコマンドを実行して、directories変数を持つmain.directories.g.dartファイルを生成する:
flutter pub run build_runner watch --delete-conflicting-outputs

これで、Widgetbookアプリを実行し、ユースケースを実際に見ることができます:

widget_bookのディレクトリにターミナルで移動して、Chromeでビルドしてみてください。Flutter Webを選択して、Runボタンでもできます。

flutter run -d chrome
  1. Widgetbookアプリにアドオンを追加して、使用するケースの外観をカスタマイズしましょう。

これだけだとわからない???

公式も意地悪で、コンポーネント作って、毎回ファイルの自動生成するコマンドを実行することを書いてません💦
Flutterの知識がある中級者向けの解説なのかもですね。

ボタンのコンポーネントでも作りますか

import 'package:flutter/material.dart';

class CoolButton extends StatelessWidget {
  const CoolButton({
    required this.buttonColor,
    required this.buttonTitle,
    required this.textColor,
    required this.fontSize,
    required this.onPressed,
    super.key
  });

  final Color buttonColor;
  final String buttonTitle;
  final Color textColor;
  final double fontSize;
  final VoidCallback onPressed;

  
  Widget build(BuildContext context) {
    return ElevatedButton(
      style: ElevatedButton.styleFrom(
        backgroundColor: buttonColor,
      ),
      onPressed: onPressed,
      child: Text(buttonTitle, style: TextStyle(color: textColor, fontSize: fontSize)),
    );
  }
}

widgetbook側の設定をする。コンストラクタ引数に値を渡しましょう。

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

// Import the widget from your app
import 'package:widget_book_tutorial/cool_button.dart';

// flutter pub run build_runner watch --delete-conflicting-outputs
// flutter run

.UseCase(name: 'Default', type: CoolButton)
Widget buildCoolButtonUseCase(BuildContext context) {
  return CoolButton(
    buttonColor: Colors.blue,
    buttonTitle: 'Submit',
    textColor: Colors.white,
    fontSize: 16,
    onPressed: () {
      print('Button pressed');
    },
  );
}

widget_bookの方のmain.dartを編集する。

// 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_workspace/main.directories.g.dart';


void main() {
  runApp(const WidgetbookApp());
}

.App()
class WidgetbookApp extends StatelessWidget {
  const WidgetbookApp({super.key});

  
  Widget build(BuildContext context) {
    return Widgetbook.material(
      directories: directories,
      addons: [
        MaterialThemeAddon(
          themes: [
            WidgetbookTheme(
              name: 'Light',
              data: ThemeData.light(),
            ),
            WidgetbookTheme(
              name: 'Dark',
              data: ThemeData.dark(),
            ),
          ],
        ),
        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,
          ],
        ),
        GridAddon(),
      ],
      appBuilder: (context, child) {
        return ScreenUtilInit(
          designSize: const Size(375, 812),
          minTextAdapt: true,
          splitScreenMode: true,
          useInheritedMediaQuery: true,
          builder: (context, child) {
            return MaterialApp(
              debugShowCheckedModeBanner: false,
              home: child,
            );
          },
          child: child,
        );
      },
    );
  }
}

flutter_screenutil追加してファイルを編集しましょう

[Flutter Webでビルドする]

iPhoneだけ表示できるようだ。なぜだ....
ヨーロッパらなら、Androidの方が多いはずだろ?、これはドイツの企業が作ったサービスのはず?

Runボタンじゃないとホットリローダができない???
コマンドでも方法があったが忘れた💦
ボタンでやり直し

色を青から赤に変えてみようと思います。

青のとき

赤に変更

最後に

コンポーネント単位で、どんなUIなのか確認できるのはありがたいですね。デザインをパーツ単位でステックホルダーの人に、見せたいことがよくあるので、導入しても良いかなと思いました。Figmaだけで十分な気がするが.....

Discussion