Question #359MediumWidgets & UI

How can you make your application responsive?

#responsive#layout#ui#mediaquery

Answer

Overview

Making a Flutter app responsive means ensuring it looks good and works well across different screen sizes (phones, tablets, desktops) and orientations (portrait, landscape). Flutter provides several approaches to achieve responsive design.


1. MediaQuery - Get Screen Dimensions

text
MediaQuery
provides access to screen size, orientation, and device information.

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.8, // 80% of screen width
      height: height * 0.5, // 50% of screen height
      color: orientation == Orientation.portrait 
        ? Colors.blue 
        : Colors.red,
    );
  }
}

2. LayoutBuilder - Respond to Parent Constraints

text
LayoutBuilder
provides the constraints from the parent widget, useful for building widgets based on available space.

dart
LayoutBuilder(
  builder: (context, constraints) {
    if (constraints.maxWidth > 600) {
      // Tablet/Desktop layout
      return Row(
        children: [
          Expanded(child: SideBar()),
          Expanded(flex: 3, child: MainContent()),
        ],
      );
    } else {
      // Mobile layout
      return Column(
        children: [
          MainContent(),
        ],
      );
    }
  },
)

3. Responsive Breakpoints

Define breakpoints for different screen sizes.

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

class ResponsiveLayout extends StatelessWidget {
  final Widget mobile;
  final Widget? tablet;
  final Widget desktop;

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

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

// Usage
ResponsiveLayout(
  mobile: MobileHomePage(),
  tablet: TabletHomePage(),
  desktop: DesktopHomePage(),
)

4. Flexible & Expanded - Proportional Layouts

Use

text
Flexible
and
text
Expanded
to create layouts that adapt to available space.

dart
Row(
  children: [
    Expanded(
      flex: 2,
      child: Container(color: Colors.red),
    ),
    Expanded(
      flex: 1,
      child: Container(color: Colors.blue),
    ),
  ],
)

5. AspectRatio - Maintain Proportions

dart
AspectRatio(
  aspectRatio: 16 / 9,
  child: Container(
    color: Colors.purple,
    child: Center(child: Text('16:9 Video')),
  ),
)

6. FractionallySizedBox - Percentage-based Sizing

dart
FractionallySizedBox(
  widthFactor: 0.8, // 80% of parent width
  heightFactor: 0.5, // 50% of parent height
  child: Container(color: Colors.green),
)

7. OrientationBuilder - Portrait vs Landscape

dart
OrientationBuilder(
  builder: (context, orientation) {
    return GridView.count(
      crossAxisCount: orientation == Orientation.portrait ? 2 : 4,
      children: List.generate(20, (index) => Card(child: Text('$index'))),
    );
  },
)

8. Responsive Packages

responsive_builder

dart
import 'package:responsive_builder/responsive_builder.dart';

ScreenTypeLayout.builder(
  mobile: (context) => MobileView(),
  tablet: (context) => TabletView(),
  desktop: (context) => DesktopView(),
)

flutter_screenutil

dart
import 'package:flutter_screenutil/flutter_screenutil.dart';

// In main()
ScreenUtil.init(
  BoxConstraints(maxWidth: 375, maxHeight: 812),
  designSize: Size(375, 812),
);

// Usage
Container(
  width: 200.w,    // Responsive width
  height: 100.h,   // Responsive height
  padding: EdgeInsets.all(16.r), // Responsive padding
  child: Text(
    'Hello',
    style: TextStyle(fontSize: 24.sp), // Responsive font
  ),
)

9. Responsive Font Sizes

dart
double getResponsiveFontSize(BuildContext context, double baseSize) {
  final width = MediaQuery.of(context).size.width;
  if (width < 600) return baseSize;
  if (width < 900) return baseSize * 1.2;
  return baseSize * 1.5;
}

Text(
  'Responsive Text',
  style: TextStyle(
    fontSize: getResponsiveFontSize(context, 16),
  ),
)

10. Platform-Specific Layouts

dart
import 'dart:io';

Widget build(BuildContext context) {
  if (Platform.isAndroid || Platform.isIOS) {
    return MobileLayout();
  } else if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) {
    return DesktopLayout();
  } else {
    return WebLayout();
  }
}

Best Practices

PracticeDescription
Mobile-firstDesign for mobile, then scale up
Test on real devicesEmulators may not show all issues
Use constraintsPrefer
text
LayoutBuilder
over
text
MediaQuery
for widgets
Avoid hardcoded sizesUse percentages or constraints
Test orientationsAlways test portrait and landscape
Use safe areasRespect notches and system UI with
text
SafeArea

Complete Example

dart
class ResponsiveHomePage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Responsive App')),
      body: SafeArea(
        child: LayoutBuilder(
          builder: (context, constraints) {
            if (constraints.maxWidth > 900) {
              // Desktop: 3 columns
              return GridView.count(
                crossAxisCount: 3,
                padding: EdgeInsets.all(16),
                children: _buildItems(),
              );
            } else if (constraints.maxWidth > 600) {
              // Tablet: 2 columns
              return GridView.count(
                crossAxisCount: 2,
                padding: EdgeInsets.all(16),
                children: _buildItems(),
              );
            } else {
              // Mobile: 1 column
              return ListView(
                padding: EdgeInsets.all(16),
                children: _buildItems(),
              );
            }
          },
        ),
      ),
    );
  }

  List<Widget> _buildItems() {
    return List.generate(
      20,
      (index) => Card(
        child: Padding(
          padding: EdgeInsets.all(16),
          child: Text('Item $index'),
        ),
      ),
    );
  }
}

Summary

Use MediaQuery for screen info, LayoutBuilder for parent constraints, and breakpoints for device-specific layouts. Combine with Flexible/Expanded for proportional sizing and responsive packages for advanced features.

Learn more at Flutter Responsive Design.