Responsive Layouts with LayoutBuilder

Responsive Layouts with LayoutBuilder

What You’ll Learn

How to build truly responsive Flutter UIs that adapt to different screen sizes using LayoutBuilder - a powerful widget that provides parent widget constraints so you can make intelligent layout decisions at runtime.

Why LayoutBuilder Matters

While MediaQuery tells you about the entire screen, LayoutBuilder tells you about the specific space available to your widget. This makes it perfect for creating reusable components that adapt to their container, whether that’s a small card, a sidebar, or the full screen.

Example

Here’s how to build a card that switches between column and row layouts based on available width:

import 'package:flutter/material.dart';

class AdaptiveCard extends StatelessWidget {
  final String title;
  final String description;
  final IconData icon;

  const AdaptiveCard({
    Key? key,
    required this.title,
    required this.description,
    required this.icon,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: LayoutBuilder(
          builder: (context, constraints) {
            // Switch layout based on available width
            final isWide = constraints.maxWidth > 300;
            
            if (isWide) {
              // Horizontal layout for wide containers
              return Row(
                children: [
                  Icon(icon, size: 48),
                  const SizedBox(width: 16),
                  Expanded(
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(title, 
                          style: Theme.of(context).textTheme.titleLarge),
                        const SizedBox(height: 4),
                        Text(description),
                      ],
                    ),
                  ),
                ],
              );
            } else {
              // Vertical layout for narrow containers
              return Column(
                children: [
                  Icon(icon, size: 48),
                  const SizedBox(height: 12),
                  Text(title, 
                    style: Theme.of(context).textTheme.titleLarge),
                  const SizedBox(height: 4),
                  Text(description, textAlign: TextAlign.center),
                ],
              );
            }
          },
        ),
      ),
    );
  }
}

// Usage example
class ResponsiveDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('LayoutBuilder Demo')),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            // Full width - will use Row layout
            AdaptiveCard(
              title: 'Wide Card',
              description: 'This card has plenty of space',
              icon: Icons.laptop,
            ),
            const SizedBox(height: 16),
            // Constrained width - will use Column layout
            SizedBox(
              width: 250,
              child: AdaptiveCard(
                title: 'Narrow Card',
                description: 'This card is constrained',
                icon: Icons.phone_android,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

How It Works

The LayoutBuilder widget passes BoxConstraints to its builder function, which includes:

You can use these constraints to make intelligent decisions about your UI structure, spacing, font sizes, and more.

Try It Yourself

Challenge: Extend the AdaptiveCard to support three layouts:

  1. Compact (< 250px): Icon on top, minimal text
  2. Normal (250-400px): The row layout from the example
  3. Expanded (> 400px): Add more details like a subtitle and action button

Bonus: Create a responsive grid that shows 1, 2, or 3 columns based on available width using LayoutBuilder inside a GridView.builder.

Tip of the Day

LayoutBuilder vs MediaQuery: Use MediaQuery.of(context).size when you need to know about the entire screen (like showing/hiding a drawer). Use LayoutBuilder when building reusable widgets that should adapt to their container size. This makes your components truly modular and testable at any size!

Performance note: LayoutBuilder rebuilds whenever its constraints change. For expensive widgets, consider using const constructors or memoization to avoid unnecessary rebuilds.