You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter自定义可滚动动态高度有机网格视图问题求助

解决CustomMultiChildLayout实现的网格无法滚动的问题

要解决这个问题,核心是让CustomMultiChildLayout拥有明确的尺寸,这样滚动组件才能正确计算可滚动区域。具体修改如下:

  1. 在CustomGridDelegate中计算总高度并设置布局尺寸
    CustomMultiChildLayout的尺寸由MultiChildLayoutDelegate的performLayout方法决定,必须调用setSize明确布局的总大小。我们根据子组件数量计算网格总高度,再设置给布局。

  2. 将网格组件包裹在SingleChildScrollView中
    有了明确的布局高度后,用SingleChildScrollView包裹网格组件,即可实现滚动功能。

  3. 修正子组件高度计算
    原代码中childHeight硬编码为60,但实际子组件(ContainerWidget)包含上下各8的padding,加上容器本身50的高度,总高度应为66,修正此数值可避免布局重叠。

修改后的完整代码:

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(widget.title)),
      body: Column(
        children: [
          Expanded(
            child: OrganicGrid(count: 50),
          ),
        ],
      ),
    );
  }
}

class OrganicGrid extends StatelessWidget {
  final int count;
  const OrganicGrid({
    required this.count,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: CustomGrid(
        children: List.generate(count, (index) => ContainerWidget(id: index + 1)),
      ),
    );
  }
}

class ContainerWidget extends StatelessWidget {
  final int id;
  const ContainerWidget({
    required this.id,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 8),
      child: Container(
        height: 50,
        decoration: const BoxDecoration(
          color: Colors.green,
        ),
        child: Center(
            child: Text(
          "Text $id",
          style: TextStyle(color: Colors.white),
        )),
      ),
    );
  }
}

class CustomGrid extends StatelessWidget {
  final List<Widget> children;
  const CustomGrid({required this.children, super.key});

  @override
  Widget build(BuildContext context) {
    List<Widget> rows = [];

    bool oddExist = children.length % 2 != 0;

    if (oddExist) {
      rows.add(LayoutId(id: 0, child: children[0]));
    }

    if (oddExist) {
      for (int i = 1; i < children.length; i++) {
        rows.add(LayoutId(id: i, child: children[i]));
      }
    } else {
      for (int i = 1; i <= children.length; i++) {
        rows.add(LayoutId(id: i, child: children[i - 1]));
      }
    }

    return CustomMultiChildLayout(
      key: key,
      delegate: CustomGridDelegate(numRows: rows.length, oddExist: oddExist),
      children: rows,
    );
  }
}

class CustomGridDelegate extends MultiChildLayoutDelegate {
  final int numRows;
  final bool oddExist;

  CustomGridDelegate({required this.numRows, required this.oddExist});

  @override
  void performLayout(Size size) {
    const double padding = 8;
    double width = size.width - padding * 3;
    double childHeight = 66; // 50容器高度 + 上下各8的padding
    double dy = 0;
    double dx = padding;

    void childLayout(int i) {
      positionChild(i, Offset(dx, dy));
      layoutChild(
        i,
        BoxConstraints(minWidth: width / 2, maxWidth: width / 2),
      );
      if (i % 2 == 0) {
        dy += childHeight;
        dx = padding;
      } else {
        dx = width / 2;
        dx += padding * 2;
      }
    }

    void zerothChildLayout(int i) {
      positionChild(i, Offset(dx, dy));
      layoutChild(
        0,
        BoxConstraints(minWidth: width + padding),
      );
      dy += childHeight;
    }

    if (oddExist) {
      zerothChildLayout(0);
      for (int i = 1; i < numRows; i++) {
        childLayout(i);
      }
    } else {
      for (int i = 1; i <= numRows; i++) {
        childLayout(i);
      }
    }

    // 计算并设置整个布局的总尺寸
    double totalHeight;
    if (oddExist) {
      // 首行1个,剩余(numRows-1)个每2个一行
      int totalRows = 1 + (numRows - 1) ~/ 2;
      totalHeight = totalRows * childHeight;
    } else {
      // 每2个一行
      int totalRows = numRows ~/ 2;
      totalHeight = totalRows * childHeight;
    }
    setSize(Size(size.width, totalHeight));
  }

  @override
  bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) => false;
}

内容的提问来源于stack exchange,提问作者Cloud

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 17:25:31