Как сделать так, чтобы дети ряда имели одинаковую высоту, читая ограничения для детей

#flutter #flutter-layout

Вопрос:

У меня есть СливерЛист с делегатом, который возвращает строку для каждого элемента. Дизайн требует такого результата:

Желаемая планировка

Двое детей-это Текст и Изображение. Формат изображения-BoxFit.cover, и я использую гибкий, чтобы придать им желаемое соотношение ширины. Мне нужна высота границы изображения, соответствующая высоте текста, только если высота больше или равна границе ширины (чтобы она была как минимум квадратной).

Первым решением было использовать LayoutBuilder для считывания ограничений ширины изображения и поместить изображение в поле с ограничениями с минимальной высотой LayoutBuilder->ограничения->>Максимальная ширина.

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

Все хорошо до тех пор, пока текст не станет длиннее ширины изображения, в этот момент изображение остается квадратным и не растягивается на всю высоту строки:

Высота изображения не заполняет высоту строки

Вторым решением было завернуть строку во встроенный виджет Height. Но это означает, что я не могу использовать компоновщик внутри него. Поэтому я удалил конструктор макетов, и теперь я не могу получить доступ к ограничениям для установки высоты изображения. Изображение очень хорошо растягивается, чтобы заполнить всю высоту строки. До тех пор, пока текст не станет слишком коротким, из-за чего высота изображения будет ниже его ширины:

введите описание изображения здесь

Как я могу сделать этот дизайн адаптивным с переменной высотой текста?

Ответ №1:

Это можно сделать с умом, используя Stack вместо этого, потому что размер Stack будет соответствовать его непозиционным дочерним элементам, поэтому он автоматически будет соответствовать вашему Text виджету. Затем используйте Positioned виджет для отображения изображения — установите его top и bottom для того, 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]),
                ),
              ],
            ),
        ],
      ),
    );
  }
}
 

Обновить:

Вы упомянули в комментариях, что хотели бы установить «минимальную высоту». Вы можете легко сделать это в 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]),
    ),
  ],
),
 

Результат:

ДЕМОНСТРАЦИЯ

Комментарии:

1. Я уже достиг этого (см. Решение 2 в моем вопросе). Проблема заключается в том, чтобы сохранить контейнер как текста, так и изображения на минимальной высоте, равной ширине изображения прямо сейчас, чтобы изображение было по крайней мере квадратным, если не высоким (первое изображение в требуемом дизайне).

2. @shadysamir Ваше «решение 2» использует внутреннюю высоту, которая приведет к тому, что флаттер будет отображаться дважды, неэффективно. Чтобы обеспечить «минимальную высоту», вы можете просто добавить еще SizedBox один в Stack , см. Мой обновленный ответ.

3. Ты направил меня в правильном направлении. Твой ответ был почти готов. Чего не хватало, так это отзывчивости. Ширина, доступная для изображения, не обязательно равна 100. Я напишу ответ, к которому пришел, чтобы решить его за меня.

Ответ №2:

Поэтому я взял ответ 1 и сделал его отзывчивым и учитывающим направление текста (для языков RTL). Вот окончательное решение:

 Stack(
  children: [
    Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Flexible(flex: 3, child: Text(("Hello Theren" * 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,
        ),
      ),
    ),
  ],
)