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 构建移动端、桌面端和 Web 应用时,你经常需要考虑如何处理列表及其选中后的内容。根据数据的不同,你可能需要先显示另一个列表,然后才能打开详情视图。在平板电脑或桌面端,可以通过多列布局实现这一点。

在移动端,由于空间有限,你仍然需要跳转到详情屏幕。

如何使用 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