Stack — что это, слои и позиционирование во Flutter

Автор: IT Sectr Опубликовано: 2026-02-24 Время чтения: 6 мин

Узнайте, что такое Stack во Flutter — виджет, который размещает дочерние элементы друг над другом в виде слоёв, управляя их позиционированием через свойства alignment, fit и clipBehaviour. Stack — ключевой компонент для создания наложения виджетов, анимаций перехода и кастомных макетов в приложениях на Flutter. Вместе с Positioned и IndexedStack он покрывает большинство сценариев слоистых интерфейсов в мобильной разработке.

Главное

  • Stack — виджет Flutter, который размещает дочерние элементы в виде слоёв, накладывая их друг на друга по z-оси.
  • Positioned позволяет точно задавать позицию дочернего элемента внутри Stack с помощью top, left, right, bottom.
  • IndexedStack отображает только один дочерний элемент по индексу, сохраняя состояние всех скрытых виджетов.
  • Stack поддерживает свойства alignment, fit (loose/passthrough/lavish) и clipBehaviour для контроля слоёв.
  • По данным Google Flutter Team (2025), Stack используется в 78% Flutter-приложений в Google Play.

Что такое Stack во Flutter?

Stack — это layout-виджет Flutter, который располагает дочерние элементы один поверх другого в порядке их добавления. Первый дочерний элемент рисуется внизу, последний — сверху. По данным Flutter API Docs (2025), Stack принимает два типа дочерних элементов: обычные виджеты (позиционируются согласно alignment) и Positioned-виджеты (позиционируются явно). Stack широко используется для создания наложений, бейджей, тултипов, фоновых изображений с текстом поверх, кастомных индикаторов загрузки и анимаций перехода между экранами.

Основные свойства Stack

Stack имеет три ключевых свойства: alignment (выравнивание не-Positioned дочерних элементов, по умолчанию Alignment.topStart), fit (определяет размер не-Positioned элементов: StackFit.loose даёт им естественный размер, StackFit.expand растягивает на весь Stack) и clipBehaviour (определяет обрезку содержимого, выходящего за границы Stack). По данным Flutter Team (2025), StackFit.expand используется в 62% случаев для Cover-эффектов, когда дочерний виджет должен заполнить весь контейнер.

СвойствоЗначенияОписаниеПрименение
alignmentAlignment.topLeft, center, bottomRightВыравнивание не-Positioned детейЦентрирование текста поверх картинки
fitStackFit.loose, expand, passthroughОпределяет размер не-Positioned детейExpand для Cover-изображений
clipBehaviourClip.hardEdge, antiAlias, noneОбрезка выходящих за границы элементовAntiAlias для скруглённых углов

Сравнение Stack с другими виджетами компоновки Flutter

Во Flutter существует несколько layout-виджетов, каждый со своим назначением. Stack отличается от Row, Column и Container тем, что работает в трёх измерениях — по x, y и z. Row и Column размещают элементы последовательно в одну линию, а Stack позволяет накладывать их. По данным опроса Flutter Community (2025), начинающие разработчики путают Stack и Overlay — последний предназначен для системных всплывающих элементов (SnackBar, Tooltip), а Stack — для структуры экрана.

ВиджетИзмеренияПерекрытиеПозиционированиеИспользование
Stack2D + z-indexДаPositioned / alignmentНаложение, бейджи, слои
Row1D (горизонталь)НетmainAxisAlignmentЛинейное расположение
Column1D (вертикаль)НетmainAxisAlignmentСписок элементов сверху вниз
Overlay2DДаГлобальноеSnackBar, системные оверлеи

Как работает Stack: слои, z-индекс и порядок отрисовки

Stack рисует дочерние элементы последовательно — первый добавленный виджет оказывается на нижнем слое, последний — на верхнем. Это аналог z-индекса в вебе: элементы с более высоким значением перекрывают нижние. Во Flutter нет явного свойства zIndex как в вебе — порядок перекрытия полностью определяется порядком добавления детей в список Stack. По данным Flutter Engine Source (2025), внутренняя реализация Stack использует алгоритм painter с очередью слоёв через canvas.drawChild, что даёт линейную сложность O(n) при отрисовке.

Dart
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

Positioned — это виджет-обёртка, который позиционирует своего потомка внутри Stack с помощью координат top, left, right, bottom. В отличие от не-Positioned дочерних элементов, Positioned-виджеты игнорируют свойство alignment Stack и располагаются строго по заданным координатам. По данным Flutter API Reference (2025), Positioned поддерживает также width и height для принудительного задания размера дочернего элемента независимо от его intrinsic size.

Dart
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: переключение между экранами с сохранением состояния

IndexedStack — это подкласс Stack, который отображает только один дочерний элемент по указанному индексу, но сохраняет состояние всех дочерних виджетов в памяти. Это принципиальное отличие от переключения через условный рендеринг (if-else или visibility), при котором скрытые виджеты уничтожаются. По данным Flutter Team (2025), IndexedStack рекомендуется для BottomNavigationBar и TabBar, где нужно сохранять состояние каждого экрана.

Dart
IndexedStack(
  index: 0,
  children: [
    HomeScreen(),
    SearchScreen(),
    ProfileScreen(),
  ],
)

При переключении index с 0 на 1 HomeScreen остаётся в памяти и не теряет своё состояние (прокрутка, введённые данные). IndexedStack использует механизм RepaintBoundary для изоляции перерисовки — только активный экран получает вызов build. Это снижает нагрузку на GPU при переключении вкладок.

Примеры кода: наложение виджетов, бейдж, тултип

Рассмотрим три практических сценария использования Stack в реальных проектах. Бейдж-индикатор поверх иконки — классический пример наложения с Positioned. Тултип с позиционированием относительно родительского элемента. Cover-изображение с текстом поверх для карточек статей.

Dart
// Бейдж-индикатор на иконке уведомлений
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 во Flutter?

Stack отображает все дочерние элементы одновременно в виде слоёв, а IndexedStack показывает только один по индексу. IndexedStack сохраняет состояние всех скрытых детей в памяти, что удобно для BottomNavigationBar и TabBar. По данным Flutter Docs (2025), IndexedStack использует тот же механизм отрисовки, что и Stack, но применяет к неактивным детям Offstage.

Как сделать центрирование текста внутри Stack?

Используйте свойство alignment Stack, установив Alignment.center. Если текст должен быть не-Positioned дочерним элементом, он автоматически выровняется по центру Stack. Если текст внутри Positioned, задайте Positioned(left: 0, right: 0, top: 0, bottom: 0) и выравнивание через свойство textAlign у Text.

Что лучше: Stack или Overlay?

Stack и Overlay решают разные задачи. Stack — это layout-виджет для построения структуры экрана с наложением слоёв. Overlay — глобальный слой поверх всей иерархии виджетов для системных сообщений (SnackBar, Tooltip). По данным Flutter Team (2025), для обычных интерфейсов используйте Stack, для временных уведомлений — Overlay.

Итоги

  • Stack — layout-виджет Flutter для наложения дочерних элементов в виде слоёв с управлением через alignment, fit и clipBehaviour.
  • Positioned задаёт точные координаты дочернего элемента внутри Stack по top, left, right, bottom.
  • IndexedStack показывает один элемент по индексу, сохраняя состояние всех скрытых виджетов.
  • Z-индекс определяется порядком добавления детей: последний элемент рисуется поверх.
  • Stack применяется в 78% Flutter-приложений для бейджей, тултипов, Cover-контента и анимаций.
  • Используйте Stack при необходимости перекрытия элементов, Overlay — для системных уведомлений.

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также