Расскажи про систему ограничений constraint для widget
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Система ограничений Constraints в Flutter
Constraints (ограничения) — это фундаментальное понятие в Flutter, определяющее, какой размер может занимать виджет. Это часто упускаемая, но критически важная часть системы layout'а.
Что такое Constraints
Constraints — это набор правил, описывающих диапазон возможных размеров для виджета. Каждое ограничение содержит четыре значения:
class BoxConstraints {
final double minWidth;
final double maxWidth;
final double minHeight;
final double maxHeight;
}
Ограничение говорит: "Твой размер должен быть между (minWidth, minHeight) и (maxWidth, maxHeight)".
Как работает система Constraints
Flutter использует нисходящую систему传播 constraints:
1. Parent говорит Child: "Вот твои ограничения"
└─ BoxConstraints(minWidth: 0, maxWidth: 300, minHeight: 0, maxHeight: 100)
2. Child выбирает размер в этих ограничениях
└─ size = Size(280, 80)
3. Parent размещает Child на основе выбранного размера
└─ Positition(x: 10, y: 20, width: 280, height: 80)
Основные типы Constraints
Tight constraints — минимум равен максимуму (нет выбора):
BoxConstraints.tight(Size(300, 200))
// minWidth: 300, maxWidth: 300
// minHeight: 200, maxHeight: 200
Обычно используются для точного позиционирования (Positioned в Stack).
Loose constraints — минимум равен нулю:
BoxConstraints(
minWidth: 0,
maxWidth: 300,
minHeight: 0,
maxHeight: 200,
)
Виджет может быть любого размера до максимума.
Unbounded constraints — нет верхнего предела:
BoxConstraints(minWidth: 0, maxWidth: double.infinity)
Это проблемно и вызывает ошибку, если виджет не может работать с бесконечностью.
Практические примеры
Проблема: ListView в Row
Этот код выбросит ошибку:
Row(
children: [
ListView(
children: [...],
),
],
)
Почему? Row передаёт ListView максимальную ширину, но максимальную высоту = бесконечность. ListView не знает, какой высоты ему быть, и выбросит ошибку.
Решение: Wrap ListView в Expanded или задать явный размер:
Row(
children: [
Expanded(
child: ListView(
children: [...],
),
),
],
)
Разбор: Expanded говорит ListView: "Займи всю доступную высоту" (tight constraint по высоте).
Проблема: бесконечный размер
Column(
children: [
ListView(
children: [...],
),
],
)
Так как Column имеет infinite высоту, ListView получит unbounded constraint и упадёт. Решение:
Column(
children: [
Expanded(
child: ListView(
children: [...],
),
),
],
)
Пример: текст в Container
Container(
width: 200,
child: Text(
"Очень длинный текст",
maxLines: 1,
),
)
Container передаёт Text ограничение: maxWidth = 200. Text выбирает размер 200x24 (если это подходит).
Кастомный виджет с Constraints:
class MyResponsiveWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
// Получаем ограничения от parent'а
if (constraints.maxWidth > 800) {
return _buildWideLayout();
} else {
return _buildCompactLayout();
}
},
);
}
}
Ключевые виджеты для работы с Constraints
ConstrainedBox — задаёт дополнительные ограничения:
ConstrainedBox(
constraints: BoxConstraints(minHeight: 100),
child: Container(color: Colors.blue),
)
SizedBox — задаёт exact размер (tight constraint):
SizedBox(
width: 100,
height: 100,
child: Container(color: Colors.red),
)
Expanded/Flexible — захватывают доступное место в Row/Column:
Row(
children: [
Expanded(flex: 2, child: Container()), // 2/3
Expanded(flex: 1, child: Container()), // 1/3
],
)
AspectRatio — поддерживает соотношение сторон:
AspectRatio(
aspectRatio: 16 / 9,
child: Container(color: Colors.blue),
)
LayoutBuilder — получает constraints и адаптируется:
LayoutBuilder(
builder: (context, constraints) {
return Column(
children: [
SizedBox(
width: constraints.maxWidth * 0.5, // 50% ширины parent'а
child: Container(),
),
],
);
},
)
Отладка Constraints
Для отладки используйте debugPrintBeginFrame или выведите constraints в LayoutBuilder:
LayoutBuilder(
builder: (context, constraints) {
print('Max width: ${constraints.maxWidth}');
print('Max height: ${constraints.maxHeight}');
return Container();
},
)
Или используйте DevTools Inspector для визуализации bounds виджетов.
Частые ошибки
- ListView/GridView в Column без Expanded — unbounded height
- Text без родителя с constraints — может занять всю ширину экрана
- Забывают про Expanded в Row — дочерние виджеты мнутся
- Используют SizedBox вместо ConstrainedBox — теряют гибкость
Принципы
- Constraints текут вниз (от parent'а к child'у)
- Размер текет вверх (child сообщает parent'у свой размер)
- Parent позиционирует child'ей на основе их размера
- Если constraints непонятны, используйте LayoutBuilder для инспекции
Понимание constraint системы — это ключ к созданию адаптивных и правильно работающих layout'ов в Flutter.