如何通过阅读孩子约束使一行的孩子具有相同的高度

Posted

技术标签:

【中文标题】如何通过阅读孩子约束使一行的孩子具有相同的高度【英文标题】:How to make children of a Row have the same height by reading child constraints 【发布时间】:2021-11-27 02:41:35 【问题描述】:

我有一个带有代表的 SliverList,它为每个项目返回 Row。设计需要这个结果:

这两个孩子是Text和Image。 Image fir 是 BoxFit.cover,我使用 Flexible 为它们提供所需的宽度比。仅当高度大于或等于宽度边界时,我才需要图像边界高度来匹配文本高度(以使其至少保持一个正方形)。

第一个解决方案是使用 LayoutBuilder 读取图像宽度的约束,并将图像放在一个 ConstrainedBox 中,minHeight 为 LayoutBuilder->constraints->maxWidth。

LayoutBuilder(
  builder: (context, constraints) => ConstrainedBox(
    constraints: BoxConstraints(
        minHeight: constraints.maxWidth,
        maxHeight: constraints.maxWidth),
    child: CachedNetworkImage(
        imageUrl: "https://via.placeholder.com/400x300",
        fit: BoxFit.cover,
      ),
  ),
)

一切都很好,直到文本长于图像宽度,此时图像仍然是正方形并且不会拉伸到整个行高:

第二种解决方案是将 Row 包装在 IntrinsicHeight 小部件中。但这意味着我不能在其中使用 LayoutBuilder。所以我删除了布局构建器,现在我无法访问约束来设置图像 minHeight。图像很好地伸展以填充整个行高度。直到文本太短,导致图像高度低于其宽度:

如何构建此设计以响应可变文本高度?

【问题讨论】:

【参考方案1】:

所以我选择了答案 1 并使其具有响应性和文本方向意识(针对 RTL 语言)。这是最终的解决方案:

Stack(
  children: [
    Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Flexible(flex: 3, child: Text(("Hello There\n" * 10))),
        const Flexible(
          flex: 1,
          child: AspectRatio(
            aspectRatio: 1,
          ),
        )
      ],
    ),
    Positioned.fill(
      child: FractionallySizedBox(
        widthFactor: 0.25,
        alignment: AlignmentDirectional.topEnd,
        child: Image.network(
          'https://via.placeholder.com/600/92c952',
          fit: BoxFit.cover,
          // height: double.infinity,
        ),
      ),
    ),
  ],
)

【讨论】:

【参考方案2】:

这可以巧妙地使用Stack 来完成,因为Stack 的大小将匹配其未定位的子元素,因此它会自动匹配您的Text 小部件。然后使用Positioned 小部件显示图像 - 将其topbottom 设置为0 以使其垂直拉伸,设置right: 0 使其对齐,并为其提供固定宽度。

示例源代码:

class Test extends StatelessWidget 
  const Test(Key? key) : super(key: key);

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(title: Text("Flutter Demo")),
      body: ListView(
        children: [
          for (int i = 8; i < 40; i += 6)
            Stack(
              children: [
                Padding(
                  padding: EdgeInsets.only(right: 100),
                  child: Text("Lorem ipsum " * i),
                ),
                Positioned(
                  top: 0,
                  bottom: 0,
                  right: 0,
                  width: 100,
                  child: Container(color: Colors.primaries[i % 10]),
                ),
              ],
            ),
        ],
      ),
    );
  


更新:

您在 cmets 中提到,您希望强制执行“最小高度”。您可以在Stack 中轻松做到这一点,方法是放置一个不可见的SizedBox,例如:

Stack(
  children: [
    Padding(
      padding: EdgeInsets.only(right: 100),
      child: Text("Lorem ipsum " * i),
    ),
    SizedBox(height: 100), // this forces minimum height
    Positioned(
      top: 0,
      bottom: 0,
      right: 0,
      width: 100,
      child: Container(color: Colors.primaries[i % 10]),
    ),
  ],
),

结果:

【讨论】:

我已经实现了这一点(请参阅我的问题中的解决方案 2)。问题是将文本和图像的容器保持在当前图像宽度的最小高度,以便图像即使不高也至少是正方形(所需设计中的第一张图像) @shadysamir 您的“解决方案 2”使用 IntrinsicHeight 这将导致布局两次,效率低下。要强制执行“最小高度”,您只需在 Stack 中添加另一个 SizedBox,请参阅我的更新答案。 你把我送到了正确的方向。你的答案几乎就在那里。缺少的是响应能力。图像的可用宽度不一定是 100。我会写下我达到的答案,为我解决它。

以上是关于如何通过阅读孩子约束使一行的孩子具有相同的高度的主要内容,如果未能解决你的问题,请参考以下文章

如何给所有 Row 孩子相同的高度

当孩子的高度是 wrap_content 时,约束布局正在占用全宽

具有一个静态和一个动态子级的堆栈视图?

如何获取已被约束拉伸的视图的高度? - iOS

如何正确设置列表视图的高度

Flutter中具有最小高度的扩展小部件