wheatandcatの開発ブログ

React Nativeで開発しているペペロミア & memoirの技術系記事を投稿してます

Widgetbookを導入してみた

アプリの画面数が増えると、ホットリロードが効かない画面が出てきてUI改修が難しくなることがある。その対策として、Flutter版StorybookであるWidgetbookを導入した。以下にその実装内容をまとめる。

導入したPR

実際の変更内容は以下のPRを参照。

github.com

github.com

Widgetbookとは

WidgetbookはFlutterでUIカタログを作成するためのツール。UIコンポーネントの状態を簡単に切り替えながら確認できる。 詳細は公式サイトを参照。

www.widgetbook.io

実装

初期セットアップ

公式のクイックスタートガイドを参考に導入を進めた。 docs.widgetbook.io

コマンドを実行すると、以下のようなフォルダ構成が生成される。

app/
└── widgetbook
     ├── README.md
     ├── analysis_options.yaml
     ├── android
     ├── build
     ├── ios
     ├── lib
     ├── linux
     ├── macos
     ├── pubspec.lock
     ├── pubspec.yaml
     ├── web
     ├── widgetbook.iml
     └── windows

コンポーネントの追加

自作のButtonコンポーネントをWidgetbookに追加するため、以下のコードを作成。

dart/app/widgetbook/lib/button.dart/lib/button.dart

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

// Import the widget from your app
import 'package:stockkeeper/components/button/button.dart';  // ← ここが自作のButtonコンポーネント

@widgetbook.UseCase(name: 'Default', type: Button)
Widget buildCoolButtonUseCase(BuildContext context) {
  return Button(title: 'Hello', onPressed: () {});
}

コード生成

以下のコマンドを実行し、Widgetbook用のコードを生成する。

$ dart run build_runner build -d

これにより、widgetbook/lib/main.directories.g.dartが自動生成される。

動作確認

widgetbook/lib/main.goをVSCodeで開き、F5キーを押してデバッグを実行する。Chromeが起動し、以下のようにUIカタログが表示される。

Providerを使ったMock対応

アプリでProviderを使用している場合、WidgetbookでもMockを作成して対応可能。公式ガイドを参考に実装した。

docs.widgetbook.io

以下は、ProviderをMockしてコンポーネントを表示する例。

dart/app/widgetbook/lib/components/item/input.dart

import 'package:flutter/material.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
import 'package:stockkeeper/components/item/input.dart';
import 'package:mocktail/mocktail.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:stockkeeper/providers/graphql.dart';

// Mockクラスの作成
class FakeQueryOptions extends Fake implements QueryOptions {}

class MockGraphQLClient extends Mock implements GraphQLClient {}

@widgetbook.UseCase(name: 'Default', type: Input)
Widget buildInputUseCase(BuildContext context) {
  final mockGraphQLClient = MockGraphQLClient();  // ←ProviderのMockを作成して

  when(() => mockGraphQLClient.query(any())).thenAnswer(
    (_) async => QueryResult(
        options: QueryOptions(document: gql('')),
        source: QueryResultSource.network,
        data: null),
  );

  return ProviderScope(
    overrides: [graphqlClientProvider.overrideWithValue(mockGraphQLClient)], // ←Providerを設定
    child: Input(
      onPressed: (item) {},
      loading: false,
    ),
  );
}

導入の効果

  • 各コンポーネントの状態をサイドメニューから簡単に切り替え可能
  • UI改修の効率が向上
  • Providerを使用したコンポーネントもMockで柔軟に対応可能

Widgetbookの導入により、FlutterアプリのUI開発が効率化された。画面数が多いプロジェクトでは、UIカタログを用いた開発が特に有効だと感じた。