dart 在Flutter中根据宽度动态排列按钮

enxuqcxy  于 12个月前  发布在  Flutter
关注(0)|答案(2)|浏览(144)

我想创建一个小部件,它需要一个小部件列表(在这种情况下总是按钮),并根据孩子的宽度自动将它们排列在一种网格中。当一行中有2个按钮时,每个按钮应该有50%的宽度。所以它应该是这样的:


的数据
首先,我尝试了这个代码:

Widget getOrderedButtonList(
  List<Widget> buttonList,
) {
  final List<Widget> orderedButtonList = [];

  for (int index = 0; index < buttonList.length; index = index + 2) {
    orderedButtonList.add(
      Row(
        children: [
          Expanded(child: buttonList[index]),
          if (index + 1 < buttonList.length) HmipSpacer.horizontal(),
          if (index + 1 < buttonList.length) Expanded(child: buttonList[index + 1]),
        ],
      ),
    );
  }
  return Column(
    children: orderedButtonList,
  );
}

字符串
这对于文本较小的按钮很好用,但是当文本较长时,我有问题,文本无法像这样适合按钮:



我不想在多行中显示文本(这是最简单的解决方案)。所以我需要一个解决方案,如果文本不太合适,将按钮放置在新的一行中。我尝试了flex_list库,它可以根据宽度排列小部件列表,它看起来像这样:

这几乎是我想要的。一排的3个按钮也不错。但是,按钮现在都有不同的宽度。我希望按钮总是具有相同的宽度,一旦不再适合,它们就被包裹起来。
在这个例子中,它应该看起来像这样:

有人知道怎么做吗?

h22fl7wq

h22fl7wq1#

我发现这
hasTextOverflow()
方法How to check if Flutter Text widget was overflowed
在这个实现中,我们检查小部件是否适合空间或是否溢出。我们还必须检查它是否使行中的前一个小部件溢出。如果没有,那么我们将项目添加到行中。如果是,那么我们创建一个新行。
这段代码需要一些重构,而且我还没有测试它是如何与填充。

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Example',
      home: ButtonsPage(),
    );
  }
}

class ButtonsPage extends StatefulWidget {
  @override
  _ButtonsPageState createState() => _ButtonsPageState();
}

class _ButtonsPageState extends State<ButtonsPage> {
  final List<String> _buttonTexts = [
    "short 1",
    "short 2",
    "looooooooooog text 1",
    "short 3",
    "short 4",
    "short 5",
    "looooooooooog text 2",
    "1",
    "2",
    "3",
    "4",
    "5",
    "6",
    "7",
    "looooooooooog text 3",
  ];

  @override
  Widget build(BuildContext context) {
    const double widgetMaxWidth = 300;
    return Scaffold(
      appBar: AppBar(
        title: const Text('Test'),
      ),
      body: Center(
        child: Container(
          color: Colors.red,
          width: widgetMaxWidth,
          child: Column(children: buildRows(_buttonTexts, widgetMaxWidth)),
        ),
      ),
    );
  }

  List<Row> buildRows(List<String> texts, double widgetMaxWidth) {
    List<Row> rows = [];
    List<String> textsInRow = [];
    for (var text in texts) {
      // We have to check does the current widget overflow but in addition to this
      // we have to check does this cause previous widgets to overflow.
      bool overflows = hasTextOverflow(text, const TextStyle(fontSize: 20),
          maxWidth: widgetMaxWidth / (textsInRow.length + 1), maxLines: 1);
      for (var textInRow in textsInRow) {
        overflows = overflows ||
            hasTextOverflow(textInRow, const TextStyle(fontSize: 20),
                maxWidth: widgetMaxWidth / (textsInRow.length + 1),
                maxLines: 1);
      }

      if (overflows) {
        rows.add(Row(
          children: _createButtons(textsInRow),
        ));
        textsInRow = [];
      }
      textsInRow.add(text);
    }
    if (textsInRow.isNotEmpty) {
      rows.add(Row(
        children: _createButtons(textsInRow),
      ));
    }
    return rows;
  }

  List<Widget> _createButtons(List<String> texts) {
    List<Widget> buttons = [];
    for (var textInRow in texts) {
      buttons.add(Expanded(
        child: ElevatedButton(
          onPressed: () {},
          child: Text(
            textInRow,
            style: const TextStyle(fontSize: 20),
          ),
        ),
      ));
    }
    return buttons;
  }

  bool hasTextOverflow(String text, TextStyle style,
      {double minWidth = 0,
      double maxWidth = double.infinity,
      int maxLines = 2}) {
    final TextPainter textPainter = TextPainter(
      text: TextSpan(text: text, style: style),
      maxLines: maxLines,
      textDirection: TextDirection.ltr,
    )..layout(minWidth: minWidth, maxWidth: maxWidth);
    return textPainter.didExceedMaxLines;
  }
}

字符串
此代码的输出如下所示:

n8ghc7c1

n8ghc7c12#

@MaLa的答案可以通过一点重构来实现,但我已经找到了另一种解决方案。我稍微修改了flex_list库中的performlayout方法,以便行中的所有元素都具有相同的宽度。如果有人也需要这个,下面是代码:

@override
  void performLayout() {
    final List<_RowMetrics> rows = [];
    // Determine Widths
    var child = firstChild;
    var rowWidth = 0.0;
    var rowMaxHeight = 0.0;
    var rowItemNumber = 0;

    final childConstraint = BoxConstraints(
      minWidth: 0,
      maxWidth: constraints.maxWidth,
      minHeight: 0,
      maxHeight: constraints.maxHeight,
    );

    while (child != null) {
      final size = child.getDryLayout(childConstraint);
      final parentData = child.parentData! as _FlexListParentData.._initSize = size;
      final neededWidth = size.width + horizontalSpacing;

      if (constraints.maxWidth - rowWidth < neededWidth ||
          constraints.maxWidth / (rowItemNumber + 1) < neededWidth) {
        // add to row to rows
        final rowSize = Size(rowWidth, rowMaxHeight);
        rows.add(_RowMetrics(rowItemNumber, rowSize));
        // reset row with first new element
        rowWidth = 0;
        rowMaxHeight = 0.0;
        rowItemNumber = 0;
      }

      parentData._rowIndex = rows.length;
      rowWidth += neededWidth;
      rowMaxHeight = rowMaxHeight > size.height ? rowMaxHeight : size.height;
      rowItemNumber++;
      child = parentData.nextSibling;
    }

    final rowSize = Size(rowWidth, rowMaxHeight);
    rows.add(_RowMetrics(rowItemNumber, rowSize));

    // position childs
    child = firstChild;
    var offsetX = 0.0;
    var offsetY = 0.0;

    for (int i = 0; i < rows.length; ++i) {
      final row = rows[i];
      while (child != null) {
        final _FlexListParentData childParentData = child.parentData! as _FlexListParentData;

        if (childParentData._rowIndex != i) {
          break;
        }

        num lastItemPadding = 0;
        if (row.childNumber > 1) {
          lastItemPadding = (horizontalSpacing * (row.childNumber - 1)) / row.childNumber;
        }

        final finalChildWidth = constraints.maxWidth / row.childNumber - lastItemPadding;

        final consts = BoxConstraints.expand(
          width: finalChildWidth,
          height: childParentData._initSize.height,
        );
        child.layout(consts);

        childParentData.offset = Offset(offsetX, offsetY);
        offsetX += finalChildWidth + horizontalSpacing;

        child = childParentData.nextSibling;
      }

      offsetX = 0.0;
      // don't add space to last row
      final vertSpacing = i + 1 == rows.length ? 0 : verticalSpacing;
      offsetY += row.contentRawSize.height + vertSpacing;
    }

    size = Size(constraints.maxWidth, offsetY);
  }

字符串

相关问题