Build Responsive LayoutBuilder and Adaptive GridView Layouts
Construct adaptive multi-screen dashboards that gracefully adjust column counts, padding, and font scales across 360px mobile, 768px tablet, and 1440px desktop displays.
1. The Core Challenge & Problem
Hardcoding fixed column counts (e.g. crossAxisCount: 3) or absolute pixel widths causes devastating layout bugs. On compact 360px mobile devices, three columns become unreadably squished and trigger yellow RenderFlex overflow stripes. Conversely, on wide 1440px desktop monitors, the same cards stretch unnaturally into giant banners with distorted aspect ratios.
2. Architectural Principles & Resolution
Modern adaptive Flutter architecture utilizes two complementary techniques: 1. LayoutBuilder Over MediaQuery: Prefer LayoutBuilder over MediaQuery.of(context).size.width when designing components. MediaQuery returns the full window width, which breaks when your component is placed inside split-screen tablet drawers, desktop sidebars, or modal dialogs. LayoutBuilder provides the exact constraints.maxWidth available to that specific widget. 2. SliverGridDelegateWithMaxCrossAxisExtent: Instead of fixed column counts, use maxCrossAxisExtent: 280. The Flutter framework automatically calculates the optimal number of columns based on available space (1 column on mobile, 2 on tablet, 4 on desktop) without hardcoded switch statements. 3. Adaptive Breakpoint Tokens: Group breakpoint thresholds into clean constants: - Compact / Mobile: < 600px - Medium / Tablet: 600px β 1024px - Expanded / Desktop: > 1024px
3. Complete Flutter Code
main.dart (Flutter 3.x+ ready)import 'package:flutter/material.dart';
void main() => runApp(const MaterialApp(home: ResponsiveDashboardPage()));
class ResponsiveDashboardPage extends StatelessWidget {
const ResponsiveDashboardPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('T08.01: Responsive LayoutBuilder')),
body: LayoutBuilder(
builder: (context, constraints) {
// Use parent constraints rather than full window dimensions!
final isCompact = constraints.maxWidth < 600;
final isTablet = constraints.maxWidth >= 600 && constraints.maxWidth < 1024;
final padding = isCompact ? 12.0 : isTablet ? 20.0 : 32.0;
return Padding(
padding: EdgeInsets.all(padding),
child: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Text(
isCompact ? 'Mobile View (< 600px)' : isTablet ? 'Tablet View (600px - 1024px)' : 'Desktop View (> 1024px)',
style: TextStyle(
fontSize: isCompact ? 18 : 24,
fontWeight: FontWeight.bold,
color: const Color(0xFF0F172A),
),
),
),
),
// SliverGrid with maxCrossAxisExtent auto-computes optimal column counts!
SliverGrid(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 260.0,
mainAxisSpacing: 16.0,
crossAxisSpacing: 16.0,
childAspectRatio: 1.2,
),
delegate: SliverChildBuilderDelegate(
(context, index) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFE2E8F0)),
boxShadow: const [
BoxShadow(color: Color(0x0A000000), blurRadius: 10, offset: Offset(0, 4)),
],
),
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CircleAvatar(
backgroundColor: const Color(0xFFEFF6FF),
child: Icon(Icons.dashboard_customize, color: const Color(0xFF2563EB)),
),
const Spacer(),
Text('Module ${index + 1}', style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
const SizedBox(height: 4),
Text('Adaptive Card', style: TextStyle(color: Colors.grey[600], fontSize: 13)),
],
),
);
},
childCount: 8,
),
),
],
),
);
},
),
);
}
} 4. Expected Visual & Behavioral Result
Resizing the window dynamically transitions the grid from 1 column on narrow mobile (360px) to 2-3 columns on tablet (768px), and 4+ columns on wide desktop (1440px+), maintaining comfortable 16px margins with zero overflow.
5. Common Pitfalls & Traps
Pitfall #1: Using MediaQuery.of(context).size.width inside reusable cards or sidebar components
Pitfall #2: Setting fixed pixel crossAxisSpacing without scaling padding
Pitfall #3: Nesting an unconstrained GridView inside a Column without shrinkWrap or Expanded