Flutter 组件抽取:自适应每行最高 item 高度的 GridView(OptionGridView)

该示例展示了一个在Flutter中创建的OptionGridView,它能适应不同高度的选项,常用于多入口选项布局。Widget通过平均分配每个按钮宽度并根据每行的最大高度自适应,同时支持按钮间的间距设置。
摘要由CSDN通过智能技术生成

简介

自适应每行最高 item 高度的 GridView,多用于多入口选项,且选项高度会因选项 tag 的不同而不断变化的场景

效果

在这里插入图片描述

范例

class _TestPageState extends State<TestPage> {
  List<String> options = [
    '1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    '11', '12', '13', '14', '15', '16', '17', '18'
  ];

  
  void initState() {
    super.initState();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('OptionGridView')),
      body: Container(
        padding: const EdgeInsets.all(10),
        width: double.maxFinite,
        height: double.maxFinite,
        child: SingleChildScrollView(
          child: Column(
            children: [
              OptionGridView(
                itemCount: options.length,
                rowCount: 4,
                mainAxisSpacing: 10,
                crossAxisSpacing: 10,
                itemBuilder: (context, index) {
                  return Container(
                    padding: const EdgeInsets.all(10),
                    alignment: Alignment.center,
                    color: Colors.red.withOpacity(0.2),
                    child: Column(
                      children: [
                        Text(options[index]),
                        SizedBox(height: Random().nextBool() ? 30 : 10),
                        if (Random().nextBool())
                          const Icon(Icons.add_circle, size: 20),
                      ],
                    ),
                  );
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

说明

1、可用于单行及多行按钮布局
2、平均分配每个按钮宽度,自适应每行最高高度
3、可添加按钮之间的间距,可添加多行之间的间距

代码

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

/// 多按钮布局,可用于单行及多行按钮布局
/// 平均分配每个按钮宽度,自适应每行最高高度
/// 可添加按钮之间的间距,可添加多行之间的间距
class OptionGridView extends StatelessWidget {
  final int itemCount;
  final int rowCount;
  final int columnCount;
  final double mainAxisSpacing;
  final double crossAxisSpacing;
  final EdgeInsetsGeometry? padding;
  final IndexedWidgetBuilder itemBuilder;

  /// [itemCount] 按钮总个数
  /// [rowCount] 每行按钮个数
  /// [mainAxisSpacing] 行间距
  /// [crossAxisSpacing] 按钮间距
  OptionGridView({
    Key? key,
    required this.itemCount,
    required this.rowCount,
    this.mainAxisSpacing = 0.0,
    this.crossAxisSpacing = 0.0,
    this.padding = const EdgeInsets.all(0),
    required this.itemBuilder,
  })  : assert(itemCount >= 0),
        assert(rowCount > 0),
        columnCount = (itemCount / rowCount).ceil(),
        assert(mainAxisSpacing >= 0),
        assert(crossAxisSpacing >= 0),
        super(key: key);

  
  Widget build(BuildContext context) {
    return ListView.separated(
      itemCount: columnCount,
      padding: padding,
      physics: const NeverScrollableScrollPhysics(),
      shrinkWrap: true,
      controller: ScrollController(keepScrollOffset: false),
      separatorBuilder: (context, index) => SizedBox(height: mainAxisSpacing),
      itemBuilder: (context, index) => buildRow(context, index),
    );
  }

  Widget buildRow(BuildContext context, int index) {
    if (index < columnCount - 1) {
      List<Widget> row = [];
      for (int i = 0; i < rowCount; i++) {
        row.add(Expanded(
          flex: 1,
          child: itemBuilder(context, i + index * rowCount),
        ));
        if (crossAxisSpacing > 0.0 && i < rowCount - 1) {
          row.add(SizedBox(width: crossAxisSpacing));
        }
      }
      return Row(crossAxisAlignment: CrossAxisAlignment.start, children: row);
    } else {
      // 最后一行
      List<Widget> row = [];
      for (int i = 0; i < rowCount; i++) {
        int currentIndex = i + index * rowCount;
        if (currentIndex < itemCount) {
          row.add(Expanded(
            flex: 1,
            child: itemBuilder(context, i + index * rowCount),
          ));
          if (crossAxisSpacing > 0.0 && i < rowCount - 1) {
            row.add(SizedBox(width: crossAxisSpacing));
          }
        } else {
          row.add(const Expanded(flex: 1, child: SizedBox()));
        }
      }
      return Row(crossAxisAlignment: CrossAxisAlignment.start, children: row);
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值