Question #206MediumFlutter BasicsImportant

How to build responsive UI in flutter ?

#flutter

Answer

Building Responsive UI in Flutter

Creating responsive user interfaces in Flutter ensures your app looks great on devices of all sizes - from small phones to large tablets and desktops.

Core Responsive Strategies

1. MediaQuery

Get device dimensions and properties:

dart
class ResponsiveWidget extends StatelessWidget {
  
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
    final width = size.width;
    final height = size.height;
    final orientation = MediaQuery.of(context).orientation;

    return Container(
      width: width * 0.9, // 90% of screen width
      height: height * 0.5, // 50% of screen height
      child: Text('Screen: ${width.toInt()} x ${height.toInt()}'),
    );
  }
}

2. LayoutBuilder

Build widgets based on parent constraints:

dart
class AdaptiveLayout extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth > 1200) {
          return DesktopLayout();
        } else if (constraints.maxWidth > 800) {
          return TabletLayout();
        } else {
          return MobileLayout();
        }
      },
    );
  }
}

3. Flexible & Expanded

Create flexible layouts:

dart
class FlexibleLayout extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Row(
      children: [
        Expanded(
          flex: 2,
          child: Container(color: Colors.blue),
        ),
        Expanded(
          flex: 1,
          child: Container(color: Colors.green),
        ),
      ],
    );
  }
}

Responsive Design Patterns

1. Breakpoint System

dart
class Breakpoints {
  static const double mobile = 600;
  static const double tablet = 900;
  static const double desktop = 1200;

  static bool isMobile(BuildContext context) =>
      MediaQuery.of(context).size.width < mobile;

  static bool isTablet(BuildContext context) =>
      MediaQuery.of(context).size.width >= mobile &&
      MediaQuery.of(context).size.width < desktop;

  static bool isDesktop(BuildContext context) =>
      MediaQuery.of(context).size.width >= desktop;
}

class ResponsiveScreen extends StatelessWidget {
  
  Widget build(BuildContext context) {
    if (Breakpoints.isDesktop(context)) {
      return _buildDesktopLayout();
    } else if (Breakpoints.isTablet(context)) {
      return _buildTabletLayout();
    } else {
      return _buildMobileLayout();
    }
  }

  Widget _buildMobileLayout() => MobileLayout();
  Widget _buildTabletLayout() => TabletLayout();
  Widget _buildDesktopLayout() => DesktopLayout();
}

2. Responsive Widget Builder

dart
class ResponsiveBuilder extends StatelessWidget {
  final Widget mobile;
  final Widget? tablet;
  final Widget? desktop;

  const ResponsiveBuilder({
    required this.mobile,
    this.tablet,
    this.desktop,
  });

  
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth >= 1200) {
          return desktop ?? tablet ?? mobile;
        } else if (constraints.maxWidth >= 800) {
          return tablet ?? mobile;
        } else {
          return mobile;
        }
      },
    );
  }
}

// Usage
ResponsiveBuilder(
  mobile: MobileView(),
  tablet: TabletView(),
  desktop: DesktopView(),
)

3. Adaptive Padding & Spacing

dart
class ResponsivePadding {
  static double getPadding(BuildContext context) {
    final width = MediaQuery.of(context).size.width;
    if (width > 1200) return 32.0;
    if (width > 800) return 24.0;
    return 16.0;
  }

  static double getFontSize(BuildContext context, double baseSize) {
    final width = MediaQuery.of(context).size.width;
    if (width > 1200) return baseSize * 1.2;
    if (width > 800) return baseSize * 1.1;
    return baseSize;
  }
}

class AdaptiveCard extends StatelessWidget {
  
  Widget build(BuildContext context) {
    final padding = ResponsivePadding.getPadding(context);
    final fontSize = ResponsivePadding.getFontSize(context, 16);

    return Container(
      padding: EdgeInsets.all(padding),
      child: Text(
        'Responsive Text',
        style: TextStyle(fontSize: fontSize),
      ),
    );
  }
}

Responsive Grid Layout

dart
class ResponsiveGrid extends StatelessWidget {
  final List<Widget> children;

  const ResponsiveGrid({required this.children});

  
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        int crossAxisCount;
        if (constraints.maxWidth > 1200) {
          crossAxisCount = 4; // Desktop: 4 columns
        } else if (constraints.maxWidth > 800) {
          crossAxisCount = 3; // Tablet: 3 columns
        } else if (constraints.maxWidth > 600) {
          crossAxisCount = 2; // Large phone: 2 columns
        } else {
          crossAxisCount = 1; // Small phone: 1 column
        }

        return GridView.builder(
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: crossAxisCount,
            crossAxisSpacing: 16,
            mainAxisSpacing: 16,
            childAspectRatio: 1,
          ),
          itemCount: children.length,
          itemBuilder: (context, index) => children[index],
        );
      },
    );
  }
}

Orientation-Aware Layouts

dart
class OrientationLayout extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return OrientationBuilder(
      builder: (context, orientation) {
        return GridView.count(
          crossAxisCount: orientation == Orientation.portrait ? 2 : 4,
          children: List.generate(20, (index) {
            return Card(
              child: Center(child: Text('Item $index')),
            );
          }),
        );
      },
    );
  }
}

Responsive Text Sizing

dart
class ResponsiveText extends StatelessWidget {
  final String text;
  final TextStyle? baseStyle;

  const ResponsiveText(this.text, {this.baseStyle});

  
  Widget build(BuildContext context) {
    final width = MediaQuery.of(context).size.width;

    double scaleFactor;
    if (width > 1200) {
      scaleFactor = 1.2;
    } else if (width > 800) {
      scaleFactor = 1.1;
    } else if (width > 600) {
      scaleFactor = 1.0;
    } else {
      scaleFactor = 0.9;
    }

    return Text(
      text,
      style: baseStyle?.copyWith(
        fontSize: (baseStyle?.fontSize ?? 14) * scaleFactor,
      ),
    );
  }
}

Complete Responsive Example

dart
class ResponsiveDashboard extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Responsive Dashboard')),
      body: LayoutBuilder(
        builder: (context, constraints) {
          if (constraints.maxWidth > 1200) {
            // Desktop: Sidebar + Content
            return Row(
              children: [
                SizedBox(width: 250, child: Sidebar()),
                Expanded(child: MainContent()),
              ],
            );
          } else if (constraints.maxWidth > 800) {
            // Tablet: Collapsed sidebar + Content
            return Row(
              children: [
                SizedBox(width: 80, child: CollapsedSidebar()),
                Expanded(child: MainContent()),
              ],
            );
          } else {
            // Mobile: Bottom nav + Content
            return Column(
              children: [
                Expanded(child: MainContent()),
                BottomNavigationBar(
                  items: [
                    BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
                    BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Search'),
                    BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
                  ],
                ),
              ],
            );
          }
        },
      ),
    );
  }
}

Best Practices

  • Use
    text
    LayoutBuilder
    for constraint-based layouts
  • Define clear breakpoints for consistency
  • Test on multiple device sizes
  • Use flexible widgets (Expanded, Flexible)
  • Avoid hardcoded pixel values
  • Consider orientation changes
  • Cache
    text
    MediaQuery
    values if used frequently

Helpful Packages

PackagePurpose
responsive_frameworkSimplified responsive design
flutter_screenutilScreen adaptation
responsive_builderBreakpoint-based layouts
auto_size_textAutomatically resize text

Important: Responsive design isn't just about different screen sizes - consider accessibility, text scaling, and orientation changes for a truly adaptive experience.

Learn more at Building Adaptive Apps.