Flutter Responsive UI

Building adaptive layouts for all screen sizes

📱 What is Responsive UI?

Responsive UI adapts your app's layout to different screen sizes and orientations, from small phones to large tablets. Flutter provides powerful tools like MediaQuery, LayoutBuilder, and flexible widgets to create interfaces that look perfect on any device, ensuring optimal user experience across all platforms and screen dimensions.


LayoutBuilder(
  builder: (context, constraints) {
    if (constraints.maxWidth > 600) {
      return DesktopLayout();
    } else {
      return MobileLayout();
    }
  },
)
                                    

Output:

📱 Mobile
💻 Desktop

Responsive Techniques

📐

MediaQuery

Get screen size and orientation

double width = 
  MediaQuery.of(context)
    .size.width;
🏗️

LayoutBuilder

Build based on parent constraints

LayoutBuilder(
  builder: (context, constraints) {
    return Container(...);
  },
)
🔄

Flexible & Expanded

Adaptive sizing in rows/columns

Expanded(
  flex: 2,
  child: Container(...),
)
📊

AspectRatio

Maintain proportions

AspectRatio(
  aspectRatio: 16 / 9,
  child: Container(...),
)

🔹 Using MediaQuery

Get screen dimensions and adapt your layout:

import 'package:flutter/material.dart';

class ResponsiveWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Get screen size
    double screenWidth = MediaQuery.of(context).size.width;
    double screenHeight = MediaQuery.of(context).size.height;
    
    // Get orientation
    Orientation orientation = MediaQuery.of(context).orientation;
    
    return Container(
      width: screenWidth * 0.8,  // 80% of screen width
      height: screenHeight * 0.5, // 50% of screen height
      color: Colors.blue,
      child: Center(
        child: Text(
          'Width: ${screenWidth.toInt()}\n'
          'Height: ${screenHeight.toInt()}\n'
          'Orientation: $orientation',
          style: TextStyle(color: Colors.white, fontSize: 16),
          textAlign: TextAlign.center,
        ),
      ),
    );
  }
}

Output:

Width: 320
Height: 568
Orientation: portrait

🔹 LayoutBuilder for Breakpoints

Create different layouts based on available space:

class ResponsiveLayout extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        // Mobile layout (< 600px)
        if (constraints.maxWidth < 600) {
          return Column(
            children: [
              Container(height: 200, color: Colors.red),
              Container(height: 200, color: Colors.green),
              Container(height: 200, color: Colors.blue),
            ],
          );
        }
        // Tablet/Desktop layout (>= 600px)
        else {
          return Row(
            children: [
              Expanded(child: Container(color: Colors.red)),
              Expanded(child: Container(color: Colors.green)),
              Expanded(child: Container(color: Colors.blue)),
            ],
          );
        }
      },
    );
  }
}

Output:

Mobile
Desktop

🔹 Flexible and Expanded Widgets

Create flexible layouts that adapt to available space:

Row(
  children: [
    // Takes 1/4 of available space
    Expanded(
      flex: 1,
      child: Container(
        height: 100,
        color: Colors.red,
        child: Center(child: Text('1')),
      ),
    ),
    
    // Takes 2/4 of available space
    Expanded(
      flex: 2,
      child: Container(
        height: 100,
        color: Colors.green,
        child: Center(child: Text('2')),
      ),
    ),
    
    // Takes 1/4 of available space
    Expanded(
      flex: 1,
      child: Container(
        height: 100,
        color: Colors.blue,
        child: Center(child: Text('1')),
      ),
    ),
  ],
)

Output:

1
2
1

🔹 Responsive Grid

Create grids that adapt to screen size:

class ResponsiveGrid extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        // Calculate columns based on width
        int columns = constraints.maxWidth > 600 ? 4 : 2;
        
        return GridView.builder(
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: columns,
            crossAxisSpacing: 10,
            mainAxisSpacing: 10,
            childAspectRatio: 1,
          ),
          itemCount: 8,
          itemBuilder: (context, index) {
            return Container(
              color: Colors.primaries[index % Colors.primaries.length],
              child: Center(
                child: Text(
                  '${index + 1}',
                  style: TextStyle(color: Colors.white, fontSize: 24),
                ),
              ),
            );
          },
        );
      },
    );
  }
}

Output:

Mobile (2 cols)
1
2
3
4
Desktop (4 cols)
1
2
3
4

🔹 Responsive Text Sizing

Scale text based on screen size:

class ResponsiveText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    double screenWidth = MediaQuery.of(context).size.width;
    
    // Calculate responsive font size
    double fontSize = screenWidth > 600 ? 32 : 20;
    
    return Text(
      'Responsive Text',
      style: TextStyle(
        fontSize: fontSize,
        fontWeight: FontWeight.bold,
      ),
    );
  }
}

// Or use percentage of screen width
double responsiveFontSize(BuildContext context) {
  double screenWidth = MediaQuery.of(context).size.width;
  return screenWidth * 0.05; // 5% of screen width
}

Output:

Mobile
Responsive Text
Desktop
Responsive Text

🔹 Responsive Best Practices

Design Guidelines:

  • Breakpoints: Use 600px for mobile/tablet, 900px for tablet/desktop
  • Test on devices: Always test on real devices and emulators
  • Flexible layouts: Use Expanded and Flexible instead of fixed sizes
  • Orientation: Handle both portrait and landscape modes
  • Safe areas: Use SafeArea widget for notches and system UI
  • Minimum sizes: Set minimum tap targets of 48x48 pixels
// Common breakpoints
bool isMobile(BuildContext context) {
  return MediaQuery.of(context).size.width < 600;
}

bool isTablet(BuildContext context) {
  double width = MediaQuery.of(context).size.width;
  return width >= 600 && width < 900;
}

bool isDesktop(BuildContext context) {
  return MediaQuery.of(context).size.width >= 900;
}

// Responsive padding
double responsivePadding(BuildContext context) {
  double width = MediaQuery.of(context).size.width;
  if (width < 600) return 16;
  if (width < 900) return 24;
  return 32;
}

🧠 Test Your Knowledge

Which widget is used to get screen dimensions?