Flexible — віджет Flutter, який керує тим, як дочірній елемент займає доступний простір у Flex-контейнері. На відміну від Expanded, Flexible не примушує дитину заповнювати всю виділену область — дочірній віджет може бути меншим, і решта місця залишається порожньою. Згідно з офіційною документацією Flutter (2026), Flexible необхідний у сценаріях, де потрібен гнучкий розподіл простору без примусового розтягування: кнопки з мінімальною шириною, текстові блоки з природним розміром та елементи з intrinsic constraints.
Головне
Flexible — це віджет-обгортка, який надає дочірньому елементу гнучкий простір у Flex-контейнері. Flexible, як і Expanded, бере участь у розподілі вільного місця, але з ключовою відмінністю: дитина Flexible може зайняти менше місця, ніж їй виділено, якщо її власний розмір менший за доступний.
Flexible використовує Flex-алгоритм Flutter для обчислення доступного простору. Спочатку фреймворк опитує всіх негнучких дітей (віджети без Flexible/Expanded) і обчислює їхній intrinsic size. Решта простору ділиться між гнучкими дітьми пропорційно їхнім flex-факторам. Після виділення простору Flexible перевіряє fit — якщо він loose, до дочірнього елемента застосовується обмеження «не більше виділеного», дозволяючи йому залишитися маленьким.
Особливість Flexible: якщо дочірній віджет має власні мінімальні розміри (наприклад, Text з довгим рядком), Flexible виділяє йому запитуваний простір без примусового розтягування. Це робить Flexible незамінним для елементів, розмір яких має визначатися вмістом, а не контейнером.
Висновок: використовуйте Flexible, коли потрібно резервувати простір для елемента, але не примушувати його до розтягування. Це зберігає природні розміри вмісту при гнучкому розподілі макета.
FlexFit — перелік із двох значень: tight і loose. Від вибору fit залежить, чи буде дочірній віджет примусово розтягнутий до розмірів виділеного простору.
FlexFit.tight: дочірній елемент отримує tight constraints — мінімальний і максимальний розміри дорівнюють виділеному простору. Дитина зобов'язана зайняти рівно стільки місця, скільки виділив Flex-алгоритм. Якщо дитина менша — вона розтягується. Якщо більша — вона стискається. Поведінка ідентична Expanded.
FlexFit.loose: дочірній елемент отримує loose constraints — мінімальний розмір дорівнює 0, максимальний дорівнює виділеному простору. Дитина може бути будь-якого розміру від 0 до maximum. Якщо дитина маленька (наприклад, Text з коротким рядком), вона займає свою природну ширину, а решта простору залишається порожньою.
Висновок: FlexFit.loose — вибір для текстових блоків та елементів із натуральним розміром. FlexFit.tight — для строгих колонкових макетів, де всі елементи повинні бути однакової ширини або висоти.
Flexible і Expanded виглядають схоже, але вирішують різні завдання. Expanded завжди розтягує дитину на повний доступний простір. Flexible може залишити порожнє місце навколо дитини, якщо розмір вмісту менший за виділену частку.
Сценарій для Flexible: всередині Row розташовані дві кнопки. Перша — «Купити» (короткий текст), друга — «Додати в кошик» (довгий текст). З Expanded обидві кнопки розтягнуться на рівну ширину, і короткий текст матиме величезні відступи. З Flexible кожна кнопка займе ширину свого тексту, а вільний простір залишиться між ними або по краях.
Сценарій для Expanded: всередині Column розташовані три картки, які повинні займати рівну висоту незалежно від вмісту. Expanded рівномірно ділить доступну висоту, і кожна картка розтягується до однакового розміру. Flexible з loose тут не підійде — маленька картка залишиться маленькою, порушивши єдиний дизайн.
Висновок: вибирайте Flexible, коли вміст визначає розмір елемента і порожній простір навколо нього допустимий. Вибирайте Expanded, коли всі елементи повинні бути однакового розміру незалежно від вмісту.
Приклад 1 демонструє базове використання Flexible з FlexFit.loose у Row.
Row(
children: [
Flexible(
fit: FlexFit.loose,
child: Container(
color: Colors.amber,
child: const Text('короткий'),
),
),
Flexible(
fit: FlexFit.loose,
child: Container(
color: Colors.cyan,
child: const Text('довгий текст для демо'),
),
),
],
)
У прикладі обидва Flexible з loose fit. Перший контейнер з коротким текстом займає свою природну ширину. Другий з довгим — свою. Row виділяє кожному по половині доступної ширини, але loose fit дозволяє контейнерам бути меншими, і вільний простір залишається порожнім між ними.
Приклад 2 — порівняння Flexible з Tight і Loose fit у Column.
Column(
children: [
Flexible(
fit: FlexFit.tight,
child: const Card(
child: ListTile(
title: Text('Tight — розтягнута повна висота'),
),
),
),
const SizedBox(
height: 8,
),
Flexible(
fit: FlexFit.loose,
child: const Card(
child: ListTile(
title: Text('Loose — природна висота'),
),
),
),
],
)
У Column перший Flexible (tight) розтягує Card на половину доступної висоти. Другий Flexible (loose) — Card займає висоту за вмістом, а решта простору залишається порожньою. Візуально видно, як tight примусово заповнює область, а loose зберігає компактний розмір.
Висновок: у production-коді Flexible з loose застосовується для полів форми, кнопок і текстових блоків. Flexible з tight — еквівалентний Expanded і використовується для рівномірного розподілу.
Помилка 1: використання Flexible поза Flex-контейнером. Як і Expanded, Flexible викидає LayoutException, якщо розташований не всередині Row, Column або Flex. Рішення: завжди перевіряйте батьківський ланцюжок віджетів.
Помилка 2: плутанина між Flexible і Expanded. Розробники часто використовують Expanded, коли потрібен Flexible з loose fit, і навпаки. Результат: Expanded розтягує елемент з коротким текстом, створюючи великі порожні поля. Рішення: якщо розмір елемента може бути меншим за виділений — використовуйте Flexible(fit: FlexFit.loose).
Помилка 3: нульовий flex-фактор. Flexible з flex=0 поводиться як негнучкий віджет, що може бути неочевидно. Розробник очікує гнучкої поведінки, але елемент займає мінімальний розмір. Рішення: flex повинен бути ≥1 для активного розподілу простору.
Помилка 4: Flexible всередині ScrollView без обмеження розмірів. Якщо Flexible знаходиться в Column всередині SingleChildScrollView, scroll-контейнер дає нескінченний простір, і flex-розподіл не працює. Рішення: обмежуйте розмір scroll-контейнера через SizedBox або ConstrainedBox.
Висновок: Flexible вимагає такої ж уваги до батьківського контексту, як Expanded. Різниця тільки в fit — вибирайте його свідомо залежно від завдання.
Часті запитання
Expanded — це Flexible з фіксованим fit=tight. Різниця в тому, що Expanded має жорстку прив'язку до заповнення всього доступного простору, а Flexible може використовувати loose fit для збереження природного розміру дитини.
Для кнопок з різною довжиною тексту використовуйте Flexible(fit: FlexFit.loose). Кожна кнопка займе ширину за вмістом, і між ними не буде диспропорції. Expanded зробить усі кнопки однакової ширини.
Встановіть fit: FlexFit.tight — Flexible буде примушувати дитину заповнювати весь виділений простір. У цьому режимі Flexible повністю ідентичний Expanded за поведінкою.
Так, якщо дитина більша за виділений простір. Flexible передає constraints з максимальним розміром, що дорівнює частці Flex-контейнера. Якщо дитина перевищує цей розмір, вона стискається в межах constraints.
Так, Flexible працює з будь-яким Flex-контейнером, включаючи Row, Column і базовий віджет Flex з довільною direction. Flex дозволяє задавати нестандартні напрямки через mainAxisAlignment і crossAxisAlignment.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також