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:
dartclass 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:
dartclass 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:
dartclass 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
dartclass 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
dartclass 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
dartclass 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
dartclass 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
dartclass 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
dartclass 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
dartclass 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 for constraint-based layoutstext
LayoutBuilder - Define clear breakpoints for consistency
- Test on multiple device sizes
- Use flexible widgets (Expanded, Flexible)
- Avoid hardcoded pixel values
- Consider orientation changes
- Cache values if used frequentlytext
MediaQuery
Helpful Packages
| Package | Purpose |
|---|---|
| responsive_framework | Simplified responsive design |
| flutter_screenutil | Screen adaptation |
| responsive_builder | Breakpoint-based layouts |
| auto_size_text | Automatically 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.