#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,
),
),
),
],
)