0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Flutter】親Widgetから子Widgetへコールバックを渡して責務を分離してみた

0
Posted at

画面の処理が増え、UIにもロジックを書くことが多くなってきたため、UIと処理の責務を分離しました。
子WidgetはUIの描画とユーザー操作の通知を担当し、実際の処理は親WidgetからVoidCallbackで渡しています。

子(ItemTileなど)
├─ UIの描画
└─ タップなどのイベント通知

例1: ItemTile — タップ処理を親に委譲

親Widgetでは通知を受け取り、Firestoreの更新処理を実行します。

class ItemTile extends StatelessWidget {
  final ShoppingItem item;
  final VoidCallback onToggle;
  const ItemTile({
    super.key,
    required this.item,
    required this.onToggle,
  });
  @override
  Widget build(BuildContext context) {
    return ListTile(
      onTap: onToggle, // 親から受け取った処理を実行
      leading: Checkbox(value: item.isChecked, onChanged: null),
      title: Text(item.name),
    );
  }
}
void _toggleCheck(ShoppingItem item) {
  toggleItemCheck(item); // Firestore の update 処理
}

ItemListを経由してItemTileへコールバックを渡しています。

ItemList(
  items: foodItems,
  onToggle: _toggleCheck,
)

例2: BottomActionBar — ボタン操作を親に委譲

BottomActionBarでもボタンが押された際の処理は持たず、親Widgetから受け取ったコールバックを実行するだけにしています。

class BottomActionBar extends StatelessWidget {
  final VoidCallback onAdd;
  final VoidCallback? onSort; // null ならボタン無効
  const BottomActionBar({
    super.key,
    required this.onAdd,
    required this.onSort,
  });
  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Expanded(
          child: FilledButton.icon(
            onPressed: onAdd,
            icon: const Icon(Icons.add),
            label: const Text('追加'),
          ),
        ),
        Expanded(
          child: OutlinedButton.icon(
            onPressed: onSort, // null → 自動で disabled
            icon: const Icon(Icons.sort),
            label: const Text('並び替え'),
          ),
        ),
      ],
    );
  }
}

例3: FilterBar — 任意のコールバック

任意の引数を受け取るコールバックも同様に親へ処理を委譲できます。

class FilterBar extends StatelessWidget {
  final String selected;
  final Function(String) onChanged;
  final VoidCallback? onClearChecked; // 省略可能
  // ...
  IconButton(
    onPressed: onClearChecked == null
        ? null
        : () => _showConfirmDialog(context),
    icon: Icon(Icons.delete_sweep),
  )
}

確認ダイアログ後に親の処理を呼ぶ:

if (result == true) {
  onClearChecked?.call();
}

まとめ

  • 子WidgetはUIの描画とユーザー操作の通知のみを担当する
  • 実際の処理は親Widgetが担当し、VoidCallbackや引数付きのコールバックとして子Widgetへ渡す
  • VoidCallback?にするとnullを渡せるため、Material系ボタンを簡単に無効化できる
  • Firestore通信など状態を変更する処理を親へ集約すると、Widgetごとの責務が明確になり、UIコンポーネントを再利用しやすくなります。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?