Что такое LayoutBuilder и когда его использовать?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
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), пишешь один универсальный, который адаптируется к контексту.