Slide 1

Slide 1 text

新しい風。 SolidFlutterで実現するシンプルな状態管理 株式会社ZOZO
 ブランドソリューション開発本部 新規事業部 フロントエンドブロック
 大野 純平 Copyright © ZOZO, Inc. 1

Slide 2

Slide 2 text

© ZOZO, Inc. 株式会社ZOZO ブランドソリューション開発本部 新規事業部 フロントエンドブロック 大野 純平 2025年 株式会社ZOZO新卒入社 FlutterでのZOZOマッチアプリの開発を担当 X: junjun_1345 Github: junjun-1345 2

Slide 3

Slide 3 text

© ZOZO, Inc. 3 https://zozomatch.jp/ 3 ● 2025年6月30日提供開始 ● 全身見える直感型マッチングアプリ ● ファッションジャンル診断などの情報をもとに、ZOZO独自のAIが 「好みの雰囲気」の相手を紹介(最大20人/日) ● プロフィールに登録された全身写真からユーザー自身の雰囲気も 分析 ● 全身写真と自分らしさを表すキャッチコピーを登録するプロ フィールで、個性や魅力が一目で伝わりやすい設計 ● 公的証明書を利用した本人確認や24時間365日の監視体制

Slide 4

Slide 4 text

© ZOZO, Inc. 4 アジェンダ 1. Flutterにおける状態管理の現状 2. “新しい風” SolidFlutterとは 3. 作者の意図 4. これまでとの相違点 5. 触ってみた 6. 構造 7. 所感 8. まとめ

Slide 5

Slide 5 text

© ZOZO, Inc. 5 Flutterにおける状態管理の現状 ● Stateful Widget ● Provider (公式推奨パッケージ) ● Riverpod (日本では主流) ● Flutter Redux ● BLoC ● etc... 様々な選択肢が存在する ここに新しい風が吹いた!!!

Slide 6

Slide 6 text

© ZOZO, Inc. 6 “新しい風” SolidFlutterとは SolidFlutter は、Flutter 上に構築された小さなフレームワークで、状態管理ライブラリ ● SwiftUI と同様に、シンプルで直感的な構文でレスポンシブなコンポーネントを作成できる ● SolidFlutterとは、変数にアノテーション (@) を付けるだけで、状態管理や依存性注入が自動で行 われる ● solidartでの状態管理のボイラーテンプレートを生成する 他にも関連で ● solidart ○ dart および Flutter アプリケーション用の無料のオープンソース状態管理ライブラリ ● solidart_lint といったパッケージが存在する。

Slide 7

Slide 7 text

© ZOZO, Inc. 7 作者の意図 SwiftUIに影響を受けた(SwiftUI in 2025: Forget MVVM の記事) →FlutterでもViewModelをなくすような動きができないか アノテーションで状態管理ができるようになると、状態と UI ロジックを同じ場所にまとめて保存でき、 ViewModelが不要になるのでは !? https://dimillian.medium.com/swiftui-in-2025-forget-mvvm-262ff2bbd2ed より引用

Slide 8

Slide 8 text

© ZOZO, Inc. 8 これまでとの相違点 Solidartでは、変数にアノテーションを付けるだけで状態管理が可能。 従来のように Provider や Notifier を別で実装する必要なし @SolidState() int counter = 0; これだけで依存関係が自動追跡され、UIのうち「counter」を参照している部分だけが再描画される =シンプルな状態管理

Slide 9

Slide 9 text

© ZOZO, Inc. 9 触ってみた 公式のリファレンスに従って設定 生成コマンドを利用可能に flutter pub add solid_annotations flutter_solidart 一応、推奨のlintもインストールした dart pub global activate solid_generator パッケージのインストール

Slide 10

Slide 10 text

© ZOZO, Inc. 10 触ってみた 生成時に使用するコマンド solid [options] -s, --source 入力ディレクトリ(デフォルト: source) -o, --output 出力ディレクトリ(デフォルト: lib) -w, --[no-]watch 監視モード(変更を自動反映) -c, --[no-]clean ビルドキャッシュを削除(次回はフルビルド) -v, --[no-]verbose 詳細ログ出力 -h, --[no-]help ヘルプの表示 コマンドオプション一覧 開発時は監視コマンドを実行することが 推奨されていた実装中は裏でずっと起動

Slide 11

Slide 11 text

© ZOZO, Inc. 11 触ってみた プロジェクトのルート直下に source ディレクトリを作り、その中に main.dart を配置して実装する構成 Solid は source/ を読み取り、実行に必要なボイラープレートを自動生成して lib/ に出力 my_project/ ├─ pubspec.yaml ├── lib/ │ └─main.dart ← 自動生成 └─ source/ └─ main.dart ← 実装ファイル

Slide 12

Slide 12 text

© ZOZO, Inc. 12 触ってみた class CounterPage extends StatelessWidget { CounterPage({super.key}); @SolidState() int counter = 0; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Computed')), body: Center( child: Text('Counter: $counter'), ), floatingActionButton: FloatingActionButton( onPressed: () => counter++, child: const Icon(Icons.add), ), ); } } カウンターにおける状態管理を実装 (@SolidStateを使用) stateにはアノテーションをつける 使用時は$変数名 →非常にシンプルに状態管理 の実装ができる @SolidStateとは状態変数を 作成できるアノテーション

Slide 13

Slide 13 text

© ZOZO, Inc. 13 触ってみた class QueryExample extends StatelessWidget { QueryExample({super.key}); @SolidState() String? userId; @SolidQuery() Future fetchData() async { if (userId == null) return null; await Future.delayed(const Duration(seconds: 1)); return 'Fetched Data for $userId'; } カウンターにおける状態管理を実装 (@SolidQueryを使用) 非同期の処理の上に アノテーションをつける 関数内でも$変数が使用可能 @SolidQueryとは非同期の 状態変数を作成できるアノテーション

Slide 14

Slide 14 text

© ZOZO, Inc. 14 触ってみた Column( children: [ const Text('Complex SolidQuery example'), fetchData().when( ready: (data) { if (data == null) { return const Text('No user ID'); } return Text(data); }, loading: , error: , ), ], ), カウンターにおける状態管理を実装 (@SolidQueryを使用) アノテーションをつけた関数を使用 非同期処理の状態に基づいて条 件を出し分け ● @SolidQuery(debounce: Duration(seconds: 1))最後の 変更後、実行する前に 1 秒間 待機も可能

Slide 15

Slide 15 text

© ZOZO, Inc. 15 触ってみた 主機能一覧 ● State ○ Solid ウィジェットにリアクティブ状態変数を作成できるもの ● Effect ○ 状態変数が変更するたびに副作用をトリガーするもの ● Environment ○ ウィジェットツリーからSolidウィジェットにプロバイダを注入するもの ● Query ○ ネットワークからデータを取得するなど、非同期データ ソースに基づいてリアクティブ状態 を作成できるもの など、機能は十分揃っている。

Slide 16

Slide 16 text

© ZOZO, Inc. 16 構造理解 sourceから生成されたボイラーテンプレートがどのようになっているのかコードリーディングする 今回実装例で上げたカウンターにおける状態管理を元に解説する class CounterPage extends StatelessWidget { CounterPage({super.key}); @SolidState() int counter = 0; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Computed')), body: Center( child: Text('Counter: $counter'), ), floatingActionButton: FloatingActionButton( onPressed: () => counter++, child: const Icon(Icons.add), ), ); } }

Slide 17

Slide 17 text

© ZOZO, Inc. 17 構造理解 class CounterPage extends StatefulWidget { const CounterPage({super.key}); @override State createState() => _CounterPageState(); } class _CounterPageState extends State { final counter = Signal(0, name: 'counter'); @override void dispose() { counter.dispose(); super.dispose(); } lib/main.dartの生成ファイル @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Computed')), body: Center( child: SignalBuilder( builder: (context, child) { return Text('Counter: ${counter.value}'); }, ), ), floatingActionButton: FloatingActionButton( onPressed: () => counter.value++, child: const Icon(Icons.add), ), ); } }

Slide 18

Slide 18 text

© ZOZO, Inc. 18 構造理解 Stateful Widgetに変換される Stateful Widgetでの状態管理とは 違い、定義するときにSignalを使用 する class CounterPage extends StatefulWidget { const CounterPage({super.key}); @override State createState() => _CounterPageState(); } class _CounterPageState extends State { final counter = Signal(0, name: 'counter'); @override void dispose() { counter.dispose(); super.dispose(); } 後半のコードへ続く (変換前はStateless Widget)

Slide 19

Slide 19 text

© ZOZO, Inc. 19 構造理解 Signalとは、 Signals are the cornerstone of reactivity in solidart. They contain values that change over time; when you change a signal’s value, it automatically updates anything that uses it. Signalは、solidartの機能で、値が変わると使っている場所が自動で更新される、賢い変数 →状態管理の根幹 ex) Provider, useState

Slide 20

Slide 20 text

© ZOZO, Inc. 20 構造理解 パラメーター (new) Signal Signal( int value, { bool? equals, String? name, bool? autoDispose, bool Function(int?, int?) comparator = identical, bool? trackInDevTools, bool? trackPreviousValue, }) auto disposeも可能 devtoolsに表示するかとも操作できるのか

Slide 21

Slide 21 text

© ZOZO, Inc. 21 構造理解 SignalBuilderで変更対象Widgetが囲われている child: SignalBuilder( builder: (context, child) { return Text('Counter: ${counter.value}'); }, SignalBuilderとは A signals widget builder. Reacts to any number of signals calling the builder each time. SignalBuilderは、solidartの機能で、Signalのためのウィジェットビルダー 任意の数のSignalに反応し、変更があるたびにbuilderを呼び出す

Slide 22

Slide 22 text

© ZOZO, Inc. 22 所感 ● アノテーションは非常に便利だが、コード生成が、現時点では安定性に課題がある印象(Claude Codeで作ったみたい) → まだ実験段階のフレームワークという位置づけ ● セットアップがシンプルで、「とりあえず動く」体験がすぐ得られる ● lib/ 配下に大量の自動生成コードが作られるため、プロジェクトが大きくなると、パフォーマンス 低下やコードレビューの負担につながりそう ● アノテーションの恩恵は大きいが、生成物をどう扱うかは課題になりそう

Slide 23

Slide 23 text

© ZOZO, Inc. 23 まとめ 小規模開発やプロトタイプ用途なら、現状でもシンプルで楽しく状態管理ができる 一方で大規模開発や本番運用にはまだ時期尚早 今後の成熟に期待 実際に実装したポジトリ https://github.com/junjun-1345/flutter_solid_learn

Slide 24

Slide 24 text

© ZOZO, Inc. 24 参考文献 ● https://solidart.mariuti.com/ ● https://solid.mariuti.com/ ● Flutter in 2025: Forget MVVM, Embrace Fine-Grained Reactivity with SolidFlutter | Blog ● https://dimillian.medium.com/swiftui-in-2025-forget-mvvm-262ff2bbd2ed

Slide 25

Slide 25 text

No content