Back to tech

Flutterで使えるUIカタログを使ってみた( widgetbook編 )

5 min read
Table of Contents

お久しぶりです。みやかわです。

最近 Flutter をお勉強中です。

いい感じに作って完成!やったぜ!という日が続いていますが、やはり一度はちゃんと作ってAppストアなりに公開したいものです。

そうなると、バグをなくすためにテストやら維持をするためにアドセンス、効率よく作るにはどうすればよいか等々を考える必要があります。

今回は効率よく作る一つのものとして Flutter で使えるUIカタログについて書いていきます。

FlutterのUIカタログで有名所は widgetbook 、 storybook_flutter、 monarchplaybook かと思います。

今回は widgetbook について使い方を書いていきます。

[affi id=1]

widget book とは

widgetbook.io が作っている UIコンポーネントの管理ツールです。

TwitterでFluterのUIカタログについて検索をかけると使っている人がおおいようです。

公式がYoutubeで使い方を発信しているので大変わかりやすいです。

Widgetbook
www.youtube.com
image

早速試す

サンプルの用意

今回使うものは以下のリポジトリに入れてあります。

mySample_flutter_ui_catalog/my_sample_widget_book at main · Momijinn/mySample_flutter_ui_catalog
github.com
image

はじめは以下のような構成にします。

% tree my_sample_widget_book/lib 
my_sample_widget_book/lib
├── app.dart
├── main.dart
├── pages
│   └── main_page
│   └── main_page.dart
└── widgets
├── my_card
│   └── my_card.dart
└── my_text
└── my_text.dart

これを起動すると以下のようになります。

widgetbookのインストール

いつものようにライブラリをインストールコマンドを叩きます。

flutter pub add widgetbook --dev

1.0.2 が入りました。

## pubspec.yaml

dev_dependencies:
flutter_test:
sdk: flutter

# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^1.0.0
widgetbook: ^1.0.2

UIカタログを作る

プロジェクトのルートに widgetbook というフォルダを作成し、main.dart を作成します。

widgetbook/main.dart にUIカタログを作っていきます。

※ フォルダ名はテキトーで良いです。ここでは widgetbook というフォルダを作ります。

% tree widgetbook
widgetbook
└── main.dart

最初に空のUIカタログを作ります。

以下のコードを widgetbook/main.dartにコピペします。

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

class WidgetbookhotReload extends StatelessWidget {
  const WidgetbookhotReload({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Widgetbook(
        categories: const [], appInfo: AppInfo(name: 'Recipe App')); // name がUIカタログの題名
  }
}

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

その後、 widgetbook/main.dart を起動します。

flutter run -t widgetbook/main.dart -d chrome

コマンドを実行すると、Chromeが立ち上がり、UIカタログが表示されます。

自作したWidgetをUIカタログに乗せる

では、作成したWidgetをUIカタログに表示します。

以下のコードをコピペ

import 'package:flutter/material.dart';
import 'package:my_sample_widget_book/pages/main_page/main_page.dart';
import 'package:my_sample_widget_book/widgets/my_card/my_card.dart';
import 'package:my_sample_widget_book/widgets/my_text/my_text.dart';
import 'package:widgetbook/widgetbook.dart';

class WidgetbookhotReload extends StatelessWidget {
  const WidgetbookhotReload({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Widgetbook(
      appInfo: AppInfo(name: 'Recipe App'),
      lightTheme: ThemeData.light(),
      darkTheme: ThemeData.dark(),
      categories: [
        // widgets 関連
        WidgetbookCategory(
          name: 'widgets',
          widgets: [
            // MyCard の UIカタログ
            WidgetbookWidget(name: 'MyCard', useCases: [
              WidgetbookUseCase(
                name: 'Default',
                builder: (context) => const MyCard(),
              )
            ]),
            // MyText の UIカタログ
            WidgetbookWidget(name: 'MyText', useCases: [
              WidgetbookUseCase(
                name: 'Test1',
                builder: (context) => const MyText(param: 'test'),
              ),
              WidgetbookUseCase(
                name: 'Test2',
                builder: (context) => const MyText(param: 'hoge'),
              )
            ]),
          ],
        ),
        // Pages 関連
        WidgetbookCategory(name: 'Pages', widgets: [
          // MainPage の UIカタログ
          WidgetbookWidget(name: 'mainPage', useCases: [
            WidgetbookUseCase(
                name: 'Default', builder: (context) => const MainPage())
          ])
        ])
      ],
    );
  }
}

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

実行すると、以下のように自作したwidgetが表示されます。

もっとかんたんにUIカタログを作りたい

先程の解説でUIカタログを作ることができました。

しかしながら、Widgetを作るたびに widgetbook/main.dart を更新する必要が出てきます。

正直めんどいですよね。

ということで、widgetbook/main.dart 相当のものを自動で生成してくれる方法があります。

簡単に作ることができるライブラリのインストール

flutter pub add widgetbook_annotation --dev
flutter pub add widgetbook_generator --dev
flutter pub add build_runner --dev

インストール後の pubspec.yaml

dev_dependencies:
  flutter_test:
    sdk: flutter

  # The "flutter_lints" package below contains a set of recommended lints to
  # encourage good coding practices. The lint set provided by the package is
  # activated in the `analysis_options.yaml` file located at the root of your
  # package. See that file for information about deactivating specific lint
  # rules and activating additional ones.
  flutter_lints: ^1.0.0
  widgetbook: ^1.0.2
  widgetbook_generator: ^1.0.2
  widgetbook_annotation: ^1.0.0
  build_runner: ^2.1.5

実装してみる

はじめに、widgetbookの大枠を作っていきます。

lib/main.dart を以下のように編集。

ポイントは @WidgetbookTheme@WidgetbookApp です。

@WidgetbookTheme で テーマを設定し、 @WidgetbookApp で表示するレイアウトを定義しています。

import 'package:flutter/material.dart';
import 'package:my_sample_widget_book/app.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart';

// テーマ設定
@WidgetbookTheme.dark()
ThemeData darkTheme() => ThemeData.dark();
@WidgetbookTheme.light()
ThemeData lightTheme() => ThemeData.light();

// UIカタログの名前と出漁するデバイス設定
@WidgetbookApp(name: 'Recipe App', devices: [Apple.iPhone12, Samsung.s21ultra])
void main() {
  runApp(const App());
}

次は widget ごとにUIカタログを定義します。

MyText() を例にすると以下のような感じです。

MyText の Widget があるフォルダに my_test_widgetbook.dart を作成。

% tree lib/widgets/  
lib/widgets/
├── my_card
│   └── my_card.dart
└── my_text
    ├── my_test_widgetbook.dart
    └── my_text.dart

 my_test.widgetbook.dart のように my_test widgetbook の間をピリオドにすると自動生成しないので注意。

そして lib/widgets/my_test_widgetbook.dart に以下をコピペ

import 'package:flutter/material.dart';
import 'package:my_sample_widget_book/widgets/my_text/my_text.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart';

@WidgetbookUseCase(name: 'Test1', type: MyText) // ユースケース名
Widget myTestUseCase1(BuildContext context) {
  return const MyText(param: 'test');
}

@WidgetbookUseCase(name: 'Test2', type: MyText)
Widget myTestUseCase2(BuildContext context) {
  return const MyText(param: 'hoge');
}

これで MyTest widget の UIカタログの準備は完了です。

MyCard Widget と MainPage についても同様のことをしていきます。

作ったものは github に上がっています。

mySample_flutter_ui_catalog/my_sample_widget_book at main · Momijinn/mySample_flutter_ui_catalog
github.com
image

作成後し終えたら、早速生成していきます。

ビルドランナーを走らせてコードを自動生成します。

% flutter pub run build_runner build
[INFO] Generating build script...
[INFO] Generating build script completed, took 355ms

[INFO] Precompiling build script......
[INFO] Precompiling build script... completed, took 5.0s

[INFO] Initializing inputs
[INFO] Building new asset graph...
[INFO] Building new asset graph completed, took 504ms

[INFO] Checking for unexpected pre-existing outputs....
[INFO] Checking for unexpected pre-existing outputs. completed, took 1ms

[INFO] Running build...
[INFO] Generating SDK summary...
[INFO] 2.9s elapsed, 0/8 actions completed.
[INFO] Generating SDK summary completed, took 2.9s

[INFO] 4.0s elapsed, 0/8 actions completed.
[INFO] 5.1s elapsed, 0/8 actions completed.
[INFO] 6.1s elapsed, 0/8 actions completed.
[INFO] 11.3s elapsed, 0/8 actions completed.
[INFO] Running build completed, took 11.7s

[INFO] Caching finalized dependency graph...
[INFO] Caching finalized dependency graph completed, took 36ms

[INFO] Succeeded after 11.8s with 5 outputs (24 actions)

成功すると、 lib フォルダに main.widgetbook.dart というファイルが生成されます。

こいつを開くと UIカタログが表示されます。

% flutter run -t lib/main.widgetbook.dart -d chrome

どうやらフォルダ階層に合わせてUIカタログも振り分けられるらしいようです。

ちょっとアクセスしづらい。。。

ともあれ、これで自動でUIカタログを作ることができました。

まとめ

数あるUIカタログの中で今回はwidgetbookを使ったUIカタログの方法を書いてみました。

自動でUIカタログが生成されるのはホント楽で良いです。

また今回は詳細な設定については省きました。細かくチューニングしたいという人は公式を参照してください。

それでは今度は playbookかstorybook-flutter についても書こうと思います。