← Назад к вопросам

Расскажи про систему ограничений constraint для widget

2.0 Middle🔥 151 комментариев
#Flutter виджеты

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Система ограничений 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 виджетов.

Частые ошибки

  1. ListView/GridView в Column без Expanded — unbounded height
  2. Text без родителя с constraints — может занять всю ширину экрана
  3. Забывают про Expanded в Row — дочерние виджеты мнутся
  4. Используют SizedBox вместо ConstrainedBox — теряют гибкость

Принципы

  • Constraints текут вниз (от parent'а к child'у)
  • Размер текет вверх (child сообщает parent'у свой размер)
  • Parent позиционирует child'ей на основе их размера
  • Если constraints непонятны, используйте LayoutBuilder для инспекции

Понимание constraint системы — это ключ к созданию адаптивных и правильно работающих layout'ов в Flutter.