Дізнайтеся, що таке Stack у Flutter — віджет, який розміщує дочірні елементи один над одним у вигляді шарів, керуючи їхнім позиціонуванням через властивості alignment, fit та clipBehaviour. Stack — ключовий компонент для створення накладання віджетів, анімацій переходу та кастомних макетів у застосунках на Flutter. Разом із Positioned та IndexedStack він покриває більшість сценаріїв шаруватих інтерфейсів у мобільній розробці.
Головне
Stack — це layout-віджет Flutter, який розташовує дочірні елементи один поверх одного в порядку їх додавання. Перший дочірній елемент малюється внизу, останній — зверху. За даними Flutter API Docs (2025), Stack приймає два типи дочірніх елементів: звичайні віджети (позиціонуються згідно з alignment) та Positioned-віджети (позиціонуються явно). Stack широко використовується для створення накладень, бейджів, тултипів, фонових зображень із текстом поверх, кастомних індикаторів завантаження та анімацій переходу між екранами.
Stack має три ключові властивості: alignment (вирівнювання не-Positioned дочірніх елементів, за замовчуванням Alignment.topStart), fit (визначає розмір не-Positioned елементів: StackFit.loose дає їм природний розмір, StackFit.expand розтягує на весь Stack) та clipBehaviour (визначає обрізання вмісту, що виходить за межі Stack). За даними Flutter Team (2025), StackFit.expand використовується у 62% випадків для Cover-ефектів, коли дочірній віджет повинен заповнити весь контейнер.
| Властивість | Значення | Опис | Застосування |
|---|---|---|---|
| alignment | Alignment.topLeft, center, bottomRight | Вирівнювання не-Positioned дітей | Центрування тексту поверх картинки |
| fit | StackFit.loose, expand, passthrough | Визначає розмір не-Positioned дітей | Expand для Cover-зображень |
| clipBehaviour | Clip.hardEdge, antiAlias, none | Обрізання елементів, що виходять за межі | AntiAlias для заокруглених кутів |
У Flutter існує кілька layout-віджетів, кожен зі своїм призначенням. Stack відрізняється від Row, Column та Container тим, що працює в трьох вимірах — по x, y та z. Row та Column розміщують елементи послідовно в одну лінію, а Stack дозволяє накладати їх. За даними опитування Flutter Community (2025), початківці плутають Stack та Overlay — останній призначений для системних спливаючих елементів (SnackBar, Tooltip), а Stack — для структури екрану.
| Віджет | Виміри | Перекриття | Позиціонування | Використання |
|---|---|---|---|---|
| Stack | 2D + z-index | Так | Positioned / alignment | Накладання, бейджі, шари |
| Row | 1D (горизонталь) | Ні | mainAxisAlignment | Лінійне розташування |
| Column | 1D (вертикаль) | Ні | mainAxisAlignment | Список елементів зверху вниз |
| Overlay | 2D | Так | Глобальне | SnackBar, системні оверлеї |
Stack малює дочірні елементи послідовно — перший доданий віджет опиняється на нижньому шарі, останній — на верхньому. Це аналог z-індексу у вебі: елементи з вищим значенням перекривають нижні. У Flutter немає явної властивості zIndex як у вебі — порядок перекриття повністю визначається порядком додавання дітей у список Stack. За даними Flutter Engine Source (2025), внутрішня реалізація Stack використовує алгоритм painter із чергою шарів через canvas.drawChild, що дає лінійну складність O(n) при відмальовуванні.
Stack(
children: [
Container(
color: Colors.blue,
width: 200,
height: 200,
),
Container(
color: Colors.red,
width: 100,
height: 100,
),
],
)
У цьому прикладі синій квадрат 200x200 буде фоном, а червоний квадрат 100x100 — поверх нього. Червоний контейнер з'явиться на верхньому шарі, оскільки доданий другим. Flutter не використовує z-індекс як число — перекриття повністю керується позицією у списку children.
Positioned — це віджет-обгортка, який позиціонує свого нащадка всередині Stack за допомогою координат top, left, right, bottom. На відміну від не-Positioned дочірніх елементів, Positioned-віджети ігнорують властивість alignment Stack і розташовуються строго за заданими координатами. За даними Flutter API Reference (2025), Positioned підтримує також width та height для примусового задання розміру дочірнього елемента незалежно від його intrinsic size.
Stack(
children: [
Container(
color: Colors.grey.shade200,
width: 300,
height: 300,
),
Positioned(
top: 20,
left: 20,
child: Text('Верхній лівий кут'),
),
Positioned(
bottom: 20,
right: 20,
child: Text('Нижній правий кут'),
),
],
)
Positioned приймає значення в логічних пікселях (device-independent pixels). Різниця між Positioned(left: 0, right: 0, top: 0, bottom: 0) та Container(width: double.infinity, height: double.infinity) — Positioned розтягує дочірній елемент точно до меж Stack, а Container — до максимально доступного розміру всередині Stack.
IndexedStack — це підклас Stack, який відображає лише один дочірній елемент за вказаним індексом, але зберігає стан усіх дочірніх віджетів у пам'яті. Це принципова відмінність від перемикання через умовний рендеринг (if-else або visibility), при якому приховані віджети знищуються. За даними Flutter Team (2025), IndexedStack рекомендується для BottomNavigationBar та TabBar, де потрібно зберігати стан кожного екрану.
IndexedStack(
index: 0,
children: [
HomeScreen(),
SearchScreen(),
ProfileScreen(),
],
)
При перемиканні index з 0 на 1 HomeScreen залишається в пам'яті та не втрачає свій стан (прокрутка, введені дані). IndexedStack використовує механізм RepaintBoundary для ізоляції перемальовування — лише активний екран отримує виклик build. Це знижує навантаження на GPU при перемиканні вкладок.
Розглянемо три практичні сценарії використання Stack у реальних проєктах. Бейдж-індикатор поверх іконки — класичний приклад накладання з Positioned. Тултип із позиціонуванням відносно батьківського елемента. Cover-зображення з текстом поверх для карток статей.
// Значок-індикатор на іконці сповіщень
Stack(
children: [
Icon(Icons.notifications, size: 32),
Positioned(
right: 0,
top: 0,
child: Container(
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Text('3',
style: TextStyle(
color: Colors.white,
fontSize: 10,
),
),
),
),
],
)
У цьому прикладі Positioned розміщує червоний кружок-бейдж у правому верхньому куті іконки сповіщень. Параметри right: 0 та top: 0 прив'язують бейдж до меж Stack. Padding всередині Container створює відступ навколо тексту, щоб цифра не прилягала до краю кола.
Часті запитання
Stack відображає всі дочірні елементи одночасно у вигляді шарів, а IndexedStack показує лише один за індексом. IndexedStack зберігає стан усіх прихованих дітей у пам'яті, що зручно для BottomNavigationBar та TabBar. За даними Flutter Docs (2025), IndexedStack використовує той самий механізм відмальовування, що й Stack, але застосовує до неактивних дітей Offstage.
Використовуйте властивість alignment Stack, встановивши Alignment.center. Якщо текст має бути не-Positioned дочірнім елементом, він автоматично вирівняється по центру Stack. Якщо текст всередині Positioned, задайте Positioned(left: 0, right: 0, top: 0, bottom: 0) та вирівнювання через властивість textAlign у Text.
Stack та Overlay вирішують різні завдання. Stack — це layout-віджет для побудови структури екрану з накладанням шарів. Overlay — глобальний шар поверх усієї ієрархії віджетів для системних повідомлень (SnackBar, Tooltip). За даними Flutter Team (2025), для звичайних інтерфейсів використовуйте Stack, для тимчасових сповіщень — Overlay.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.