GoF以外のプログラミング・デザインパターン #21 Stateful Widget Pattern

2025/12/12

プログラミング 学習

t f B! P L
eyecatch Stateful Widget パターンは、特に Flutter(Dart)のUI設計思想から広まった概念ですが、 他のフロントエンド(React、Vue、Svelteなど)にも通じる状態をもつUIコンポーネント設計の基本パターンです。

Stateful Widget Patternとは

UI(見た目)と状態(データ)を一体化させて管理すると言うのが特徴の設計パターンです。 コンポーネント(Widget)が、自分自身の状態を保持し、それに応じて再描画(再レンダリング)する。 外部の状態管理を使わず、内部で完結するUI動作を実現できます。

構造イメージ

StatefulWidget ├─ build() → UIツリーを定義 └─ createState() → 状態クラスを生成 State (内部状態) └─ setState() → 状態更新でUI再構築
概念 意味
StatefulWidget 状態をもつUI部品
State UIの内部状態(変更可能)
setState() 状態を変更しUIを再描画
StatelessWidget 状態を持たない固定UI

サンプルコード1 : Flutterでの例(Dart)

import 'package:flutter/material.dart'; class CounterWidget extends StatefulWidget { @override _CounterWidgetState createState() => _CounterWidgetState(); } class _CounterWidgetState extends State<CounterWidget> { int count = 0; void increment() { setState(() { // 状態変更をUIに通知 count++; }); } @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('Count: $count', style: TextStyle(fontSize: 24)), ElevatedButton( onPressed: increment, child: Text('Increment'), ), ], ); } }

解説

・StatefulWidget:UI構造を持つクラス。 ・_CounterWidgetState:状態を管理するクラス。 ・setState():状態が変わったことを通知 → Flutterが自動的に再描画。

サンプルコード2 : JavaScriptでの近似例(React)

function Counter() { const [count, setCount] = React.useState(0); return ( <div style={{ textAlign: 'center' }}> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }

解説

・ReactのuseStateは、Stateful WidgetのsetState()とほぼ同じ役割。 ・Counterコンポーネント自身が「状態 + UI」を内包している。

サンプルコード3 : Javascript(Vanilla)

<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>Vanilla Stateful Widget Pattern Demo</title> <style> body { font-family: sans-serif; text-align: center; padding: 40px; } button { font-size: 16px; padding: 10px 20px; margin-top: 20px; } .counter { font-size: 32px; margin-top: 10px; } </style> </head> <body> <div id="app"></div> <script> // Stateful Widget のようなコンポーネントクラス class CounterWidget { constructor(root) { this.root = root; this.state = { count: 0 }; this.render(); } // 状態を更新して再描画 setState(newState) { this.state = { ...this.state, ...newState }; this.render(); } // コンポーネントの描画 render() { this.root.innerHTML = ` <h2>Stateful Widget Pattern (Vanilla JS)</h2> <div class="counter">${this.state.count}</div> <button id="inc">+ Increment</button> <button id="dec">- Decrement</button> `; // イベントハンドラを再登録 this.root.querySelector("#inc").onclick = () => this.setState({ count: this.state.count + 1 }); this.root.querySelector("#dec").onclick = () => this.setState({ count: this.state.count - 1 }); } } // 起動 const app = document.getElementById("app"); new CounterWidget(app); </script> </body> </html>

解説

・CounterWidget クラスが「Stateful Widget」に相当 ・this.state がコンポーネント固有の状態を保持 ・setState() メソッドで状態変更 → render() 再呼び出し ・DOM操作をラップしてReactのような再レンダリング体験を再現

メリット

・各ウィジェットが自律的に動作し、外部依存が少ない。 ・小規模UIでの状態管理が簡単。 ・再利用しやすく、デバッグも容易。

デメリット

・大規模アプリになると、状態が分散して管理が複雑化。 ・複数Widget間で状態共有が必要な場合、Statefulだけでは破綻する。 → → Flux / Redux / Riverpod / Bloc などの状態管理へ発展。

あとがき

FlutterやReactのフレームワークは、この Stateful Widgetパターン そのものと言ってもいいシステムです。 HTMLのDIM構造をブロックとして保持して、それをオブジェクトとして使うことができると言う、フロントエンド特有のパターンですね。 個人的には、Vanilla JS(素書きのJS)でも、外部に設置したHTMLファイルをテキストでAjax読み込みをして、それをデータベースなどから取得したデータのオブジェクトで一括変換する処理は実施していましたが、そこからのイベント発火ができるそれぞれのフレームワークは非常によくできていると改めて感じました。 Vanilla JSにこだわっているのは個人的な理由なので、通常はFlatterやReactなどを使う際に、このデザインパターンを意識してみると、柔軟な設計につながると思いますよ。

人気の投稿

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。

ブログ アーカイブ