flutter-master-detail-view について
Flutterアプリで一覧と選択した内容を表示するウィジェットのパターンです。画面幅に応じて、広い画面では複数列、モバイルでは詳細画面に切り替えます。
主な機能
- 01適応型マスターディテールレイアウト
- 02リストと詳細のビルダー
- 03入れ子にできるレスポンシブレイアウト
活用シーン
- 01タブレットやデスクトップで一覧と選択項目を表示
- 02モバイルで項目の詳細を開く
- 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'),
),
),
],
);
},
);
},
),
);
}
}
このウィジェットはレイアウト後にサイズを決定し、リストを可能な限り小さくしつつ、残りの部分を詳細ビューで埋めるようにします。複数のウィジェットを入れ子にして、段階的にレイアウトを適応させる際に重要です。
コメント
Markdown 対応