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

Что такое LayoutBuilder и когда его использовать?

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

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

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

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

LayoutBuilder: адаптивный дизайн на уровне layout

LayoutBuilder — это мощный виджет, который позволяет строить UI в зависимости от доступного пространства. Это фундамент адаптивного дизайна во Flutter.

Как работает LayoutBuilder

LayoutBuilder(
  builder: (BuildContext context, BoxConstraints constraints) {
    // constraints.maxWidth — доступная ширина
    // constraints.maxHeight — доступная высота
    // constraints.minWidth, minHeight — минимальные размеры
    
    if (constraints.maxWidth > 600) {
      return WideLayout(); // For tablet/desktop
    } else {
      return NarrowLayout(); // For mobile
    }
  },
)

Важно: LayoutBuilder передаёт информацию о constraints родителя. Это НЕ размер экрана, а размер доступного пространства.

Когда использовать LayoutBuilder

Случай 1: Адаптивная раскладка (responsive design)

LayoutBuilder(
  builder: (context, constraints) {
    int columns = constraints.maxWidth > 600 ? 3 : 2;
    
    return GridView.count(
      crossAxisCount: columns,
      children: List.generate(12, (i) => Card(child: Text('Item $i'))),
    );
  },
)

Вместо того, чтобы проверять размер экрана глобально, проверяем локальное доступное пространство.

Случай 2: Условный шрифт/размеры

LayoutBuilder(
  builder: (context, constraints) {
    double fontSize = constraints.maxWidth > 500 ? 24 : 16;
    
    return Text(
      'Responsive text',
      style: TextStyle(fontSize: fontSize),
    );
  },
)

Случай 3: Scaffold с адаптивным sidebar

class AdaptiveScaffold extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        children: [
          LayoutBuilder(
            builder: (context, constraints) {
              if (constraints.maxWidth > 800) {
                return SizedBox(
                  width: 250,
                  child: NavigationRail(), // Show sidebar on desktop
                );
              }
              return SizedBox.shrink(); // Hide on mobile
            },
          ),
          Expanded(
            child: MainContent(),
          ),
        ],
      ),
    );
  }
}

LayoutBuilder vs MediaQuery

LayoutBuilder (рекомендуется):

  • Работает с локальным доступным пространством
  • Компонент может быть переиспользован в разных контекстах
  • Более гибко и модульно
// ✅ Хорошо: компонент адаптируется к контексту
LayoutBuilder(
  builder: (context, constraints) {
    return SizedBox(
      width: constraints.maxWidth * 0.9,
      child: MyCard(),
    );
  },
)

MediaQuery (когда нужна информация о экране):

  • Размер всего экрана
  • Ориентация (portrait/landscape)
  • Безопасные зоны (safe insets)
// ✅ Хорошо: информация о целом экране
double screenHeight = MediaQuery.of(context).size.height;
EdgeInsets padding = MediaQuery.of(context).padding;

if (MediaQuery.of(context).orientation == Orientation.landscape) {
  return LandscapeLayout();
}

Реальный пример: адаптивная карточка

class AdaptiveCard extends StatelessWidget {
  final String title;
  final String content;
  
  const AdaptiveCard({
    required this.title,
    required this.content,
  });
  
  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        bool isMobile = constraints.maxWidth < 400;
        
        return Card(
          child: Padding(
            padding: EdgeInsets.all(isMobile ? 12 : 24),
            child: isMobile
                ? Column( // Stack vertically on mobile
                    children: [
                      Text(title, style: TextStyle(fontSize: 16)),
                      SizedBox(height: 12),
                      Text(content),
                    ],
                  )
                : Row( // Side-by-side on desktop
                    children: [
                      Expanded(
                        flex: 1,
                        child: Text(title, style: TextStyle(fontSize: 20)),
                      ),
                      SizedBox(width: 24),
                      Expanded(
                        flex: 2,
                        child: Text(content),
                      ),
                    ],
                  ),
          ),
        );
      },
    );
  }
}

Оптимизация производительности

LayoutBuilder запускает builder каждый раз, когда constraints меняются. Это может быть дорого для сложных виджетов:

// Плохо: builder вызывается часто
LayoutBuilder(
  builder: (context, constraints) {
    return ExpensiveWidget(); // Пересчитывается каждый раз
  },
)

// Хорошо: выноси логику, используй const
class AdaptiveWidget extends StatelessWidget {
  const AdaptiveWidget();
  
  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        // Только условие проверяется, тяжелая логика снаружи
        return constraints.maxWidth > 600
            ? const WideLayout()
            : const NarrowLayout();
      },
    );
  }
}

class WideLayout extends StatelessWidget {
  const WideLayout();
  
  @override
  Widget build(BuildContext context) {
    return ExpensiveWidget(); // Вычисляется один раз
  }
}

Лучшие практики

  • Используй LayoutBuilder для адаптивного UI, а не проверку размера экрана
  • Держи builder простым — выноси логику в отдельные виджеты
  • Комбинируй с Expanded, Flexible для адаптивности
  • Не пересчитывай дорогие вычисления в builder
  • Используй const конструкторы для виджетов внутри builder
  • Для инфы о целом экране — используй MediaQuery

LayoutBuilder — основа компонентного подхода в адаптивном дизайне. Вместо того, чтобы писать два разных UI (mobile/desktop), пишешь один универсальный, который адаптируется к контексту.

Что такое LayoutBuilder и когда его использовать? | PrepBro