Узнайте, что такое 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,
),
],
)
В этом примере синий квадрат 200×200 будет фоном, а красный квадрат 100×100 — поверх него. Красный контейнер появится на верхнем слое, так как добавлен вторым. 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 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также