Skip to Content
Flutter⚡ Nâng cao📱 Responsive Design

Responsive Design trong Flutter

1. MediaQuery

Lấy thông tin về screen:

@override Widget build(BuildContext context) { final size = MediaQuery.of(context).size; final width = size.width; final height = size.height; return Text('Screen: ${width}x$height'); }

Các properties hữu ích

final mediaQuery = MediaQuery.of(context); mediaQuery.size.width; // Chiều rộng mediaQuery.size.height; // Chiều cao mediaQuery.padding.top; // Safe area top (status bar) mediaQuery.padding.bottom; // Safe area bottom (home indicator) mediaQuery.orientation; // Portrait / Landscape mediaQuery.platformBrightness; // Light / Dark mode mediaQuery.textScaleFactor; // Text scale

2. LayoutBuilder

Responsive dựa trên available space (không phải screen size):

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

3. Responsive Breakpoints

class Breakpoints { static const mobile = 600; static const tablet = 900; static const desktop = 1200; } class ResponsiveBuilder extends StatelessWidget { final Widget mobile; final Widget? tablet; final Widget? desktop; const ResponsiveBuilder({ super.key, required this.mobile, this.tablet, this.desktop, }); @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, constraints) { if (constraints.maxWidth >= Breakpoints.desktop) { return desktop ?? tablet ?? mobile; } if (constraints.maxWidth >= Breakpoints.tablet) { return tablet ?? mobile; } return mobile; }, ); } } // Sử dụng ResponsiveBuilder( mobile: MobileLayout(), tablet: TabletLayout(), desktop: DesktopLayout(), )

4. OrientationBuilder

OrientationBuilder( builder: (context, orientation) { if (orientation == Orientation.portrait) { return PortraitLayout(); } else { return LandscapeLayout(); } }, )

5. FractionallySizedBox

Kích thước theo % của parent:

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

6. AspectRatio

Giữ tỷ lệ cố định:

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

7. Flexible Wrap

Wrap( spacing: 8, runSpacing: 8, children: items.map((item) { return SizedBox( width: _getItemWidth(context), child: ItemCard(item), ); }).toList(), ) double _getItemWidth(BuildContext context) { final width = MediaQuery.of(context).size.width; if (width > 900) return (width - 48) / 4; // 4 columns if (width > 600) return (width - 32) / 2; // 2 columns return width - 32; // 1 column }

8. Responsive Grid

GridView.builder( gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 300, // Tối đa 300px mỗi item mainAxisSpacing: 16, crossAxisSpacing: 16, childAspectRatio: 1.0, ), itemCount: items.length, itemBuilder: (context, index) => ItemCard(items[index]), )

9. Responsive Navigation

class ResponsiveScaffold extends StatelessWidget { final Widget body; final int selectedIndex; final Function(int) onIndexChanged; const ResponsiveScaffold({ super.key, required this.body, required this.selectedIndex, required this.onIndexChanged, }); @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, constraints) { if (constraints.maxWidth > 600) { // Tablet/Desktop: NavigationRail return Scaffold( body: Row( children: [ NavigationRail( selectedIndex: selectedIndex, onDestinationSelected: onIndexChanged, destinations: [ NavigationRailDestination( icon: Icon(Icons.home), label: Text('Home'), ), NavigationRailDestination( icon: Icon(Icons.search), label: Text('Search'), ), NavigationRailDestination( icon: Icon(Icons.person), label: Text('Profile'), ), ], ), Expanded(child: body), ], ), ); } else { // Mobile: BottomNavigationBar return Scaffold( body: body, bottomNavigationBar: BottomNavigationBar( currentIndex: selectedIndex, onTap: onIndexChanged, items: [ BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'), BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Search'), BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'), ], ), ); } }, ); } }

10. Responsive Text

Text( 'Hello', style: TextStyle( fontSize: _getResponsiveFontSize(context), ), ) double _getResponsiveFontSize(BuildContext context) { final width = MediaQuery.of(context).size.width; if (width > 1200) return 24; if (width > 600) return 20; return 16; }

Với TextScaleFactor

// Respect user's text size preference final scaleFactor = MediaQuery.of(context).textScaleFactor; final fontSize = 16 * scaleFactor;

📝 Tóm tắt

Widget/ClassMục đích
MediaQueryScreen size, padding, orientation
LayoutBuilderAvailable constraints
OrientationBuilderPortrait / Landscape
FractionallySizedBoxSize theo %
AspectRatioGiữ tỷ lệ
BreakpointWidth
Mobile< 600px
Tablet600-900px
Desktop> 900px
Last updated on