mcpField
F
flutter-master-detail-view

flutter-master-detail-view

github.com/rodydavisデザイン・UI6052 回インストール2 週間前に更新
リポジトリ

flutter-master-detail-view について

Flutterアプリで一覧と選択した内容を表示するウィジェットのパターンです。画面幅に応じて、広い画面では複数列、モバイルでは詳細画面に切り替えます。

主な機能

  1. 01適応型マスターディテールレイアウト
  2. 02リストと詳細のビルダー
  3. 03入れ子にできるレスポンシブレイアウト

活用シーン

  1. 01タブレットやデスクトップで一覧と選択項目を表示
  2. 02モバイルで項目の詳細を開く
  3. 03階層データ向けにリストと詳細画面を入れ子にする

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/rodydavis/skills --skill flutter-master-detail-view

インストール後、Claude Code で/flutter-master-detail-viewを入力して読み込まれたか確認します。

詳細

Flutter のマスターディテールビュー

Flutter でモバイル、デスクトップ、ウェブアプリケーションを構築する際、リストと項目を選択したときのコンテンツをどう表示するかが問題になることがよくあります。データによっては、詳細ビューにたどり着くまでに別のリストを表示するリストもあります。タブレットやデスクトップでは、複数列レイアウトで実現できます。

モバイルでは画面のスペースが限られているため、詳細画面に遷移する必要があります。

Flutter でマスターディテールビューを構築する方法:

import 'package:flutter/material.dart';

class MasterDetail<T> extends StatefulWidget {
  const MasterDetail({
    Key? key,
    required this.listBuilder,
    required this.detailBuilder,
    required this.onPush,
    this.emptyBuilder,
  }) : super(key: key);

  final Widget Function(BuildContext, ValueChanged<T?>, T?) listBuilder;
  final Widget Function(BuildContext, T, bool) detailBuilder;
  final void Function(BuildContext, T) onPush;
  final WidgetBuilder? emptyBuilder;

  @override
  State<MasterDetail<T>> createState() => _MasterDetailState<T>();
}

class _MasterDetailState<T> extends State<MasterDetail<T>> {
  final selected = ValueNotifier<T?>(null);
  double? detailsWidth;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      primary: false,
      body: LayoutBuilder(
        builder: (context, dimens) {
          const double minWidth = 350;
          final maxWidth = dimens.maxWidth - minWidth;
          if (detailsWidth != null) {
            if (detailsWidth! > maxWidth) {
              detailsWidth = maxWidth;
            }
            if (detailsWidth! < minWidth) {
              detailsWidth = minWidth;
            }
          }
          return ValueListenableBuilder<T?>(
            valueListenable: selected,
            builder: (context, item, child) {
              final canShowDetails = dimens.maxWidth > 800;
              final showDetails = item != null && canShowDetails;
              return Row(
                children: [
                  Expanded(
                    child: widget.listBuilder(context, (item) {
                      if (canShowDetails) {
                        selected.value = item;
                      } else {
                        selected.value = null;
                        if (item != null) widget.onPush(context, item);
                      }
                    }, selected.value),
                  ),
                  if (canShowDetails)
                    MouseRegion(
                      cursor: SystemMouseCursors.resizeLeftRight,
                      child: GestureDetector(
                        behavior: HitTestBehavior.opaque,
                        onHorizontalDragUpdate: (details) {
                          if (mounted) {
                            setState(() {
                              double w = detailsWidth ?? maxWidth;
                              w -= details.delta.dx;
                              // Check for min width
                              if (w < minWidth) {
                                w = minWidth;
                              }
                              // Check for max width
                              if (w > maxWidth) {
                                w = maxWidth;
                              }
                              detailsWidth = w;
                            });
                          }
                        },
                        child: const SizedBox(
                          width: 5,
                          height: double.infinity,
                          child: VerticalDivider(),
                        ),
                      ),
                    ),
                  if (canShowDetails)
                    SizedBox(
                      width: detailsWidth ?? maxWidth,
                      height: double.infinity,
                      child: showDetails
                          ? widget.detailBuilder(context, item, false)
                          : widget.emptyBuilder?.call(context) ??
                              const Center(
                                child: Text('Select a item to view details'),
                              ),
                    ),
                ],
              );
            },
          );
        },
      ),
    );
  }
}

このウィジェットはレイアウト後にサイズを決定し、リストを可能な限り小さくしつつ、残りの部分を詳細ビューで埋めるようにします。複数のウィジェットを入れ子にして、段階的にレイアウトを適応させる際に重要です。

コメント