Loading...
Loading...
Enforces adapting layout by available CONSTRAINTS/size, never device or platform checks — LayoutBuilder + MediaQuery.sizeOf/paddingOf/viewInsetsOf (not .of) for narrow rebuilds, Material 3 window size classes (compact <600, medium 600-840, expanded 840-1200, large >1200) as the breakpoint vocabulary, navigation affordance chosen by width (NavigationBar → NavigationRail → NavigationDrawer), list-detail single-pane-vs-two-pane, readable max-width via ConstrainedBox, Flexible/Expanded/FractionallySizedBox over fixed widths, SafeArea + display cutouts + keyboard insets, never lock orientation, foldable/hinge awareness via MediaQuery.displayFeatures, and golden-matrix verification across sizes. Use when building responsive or adaptive UI, tablet/desktop/foldable support, master-detail or two-pane screens, a NavigationRail-vs-BottomNav shell, breakpoints, LayoutBuilder, MediaQuery sizing, SafeArea/cutouts, or fixing overflow at large widths.
npx skill4agent add zakariaf/flutter-skills adaptive-layoutPlatform.isXreferences/window-size-classes.mdWindowSizeClassMediaQuery.sizeOfpaddingOfviewInsetsOf.ofreferences/list-detail-and-navigation.mdscripts/check_adaptive.shPlatform.isAndroidPlatform.isIOSkIsWebLayoutBuilderMediaQuery.sizeOf(context)<600600–840840–12001200–1600≥1600sizeOfpaddingOfviewInsetsOfviewPaddingOfMediaQuery.of(context).offlutter-performanceNavigationBarNavigationRailNavigationDrawernavigation-and-routingStatefulShellRouteSystemChrome.setPreferredOrientationsFlexibleExpandedFractionallySizedBoxSafeAreaMediaQuery.paddingOfviewPaddingOfMediaQuery.viewInsetsOfSizedBox(height: 48)accessibility-as-codewidget-compositionwidget-golden-and-a11y-testingenum WindowSizeClass { compact, medium, expanded, large, extraLarge }
WindowSizeClass windowSizeClassFor(double width) => switch (width) {
< 600 => WindowSizeClass.compact,
< 840 => WindowSizeClass.medium,
< 1200 => WindowSizeClass.expanded,
< 1600 => WindowSizeClass.large,
_ => WindowSizeClass.extraLarge,
};
// In a widget: rebuilds only when the window SIZE changes.
final sizeClass = windowSizeClassFor(MediaQuery.sizeOf(context).width);LayoutBuilderMediaQuery.sizeOf// Local constraints drive a card grid's column count.
LayoutBuilder(
builder: (context, constraints) {
final columns = constraints.maxWidth ~/ 280; // min card width, structural
return GridView.count(crossAxisCount: columns.clamp(1, 4), children: cards);
},
);// Center and cap body text to a legible measure; never full-bleed on desktop.
Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 720), // structural readable cap
child: article,
),
);
// Split a wide body proportionally, not with magic fixed widths.
Row(
children: [
Expanded(flex: 2, child: primaryPane),
Expanded(flex: 3, child: secondaryPane),
],
);// Keyboard height: rebuilds only when the keyboard shows/hides.
final keyboard = MediaQuery.viewInsetsOf(context).bottom;
// Notch/system-bar padding without subscribing to text-scale/rotation changes.
final safeTop = MediaQuery.paddingOf(context).top;
// Foldable: a vertical fold/hinge spanning the full height => split around it.
final seam = MediaQuery.displayFeaturesOf(context)
.where((f) =>
(f.type == DisplayFeatureType.fold ||
f.type == DisplayFeatureType.hinge) &&
f.bounds.top == 0 && // starts at the top edge...
f.bounds.width < f.bounds.height) // ...taller than wide => vertical
.firstOrNull; // firstOrNull: import 'package:collection/collection.dart';SafeAreabottom: falseNavigationBar// One place decides the shell chrome; screens stay affordance-agnostic.
Widget shellFor(WindowSizeClass sc, {required Widget body, required int index}) {
return switch (sc) {
WindowSizeClass.compact =>
Scaffold(body: body, bottomNavigationBar: _NavBar(index: index)),
WindowSizeClass.medium || WindowSizeClass.expanded =>
Scaffold(body: Row(children: [_Rail(index: index), Expanded(child: body)])),
WindowSizeClass.large || WindowSizeClass.extraLarge =>
Scaffold(body: Row(children: [_Drawer(index: index), Expanded(child: body)])),
};
}examples/adaptive_scaffold.dartexamples/list_detail_pane.dartif (Platform.isIOS) ... else ...MediaQuery.sizeOf(context).widthMediaQuery.of(context).size.widthMediaQuery.sizeOf(context)SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])Container(width: 375)SizedBox(width: 320)ExpandedFlexibleFractionallySizedBoxConstrainedBoxbool isTablet = MediaQuery.of(context).size.shortestSide > 600WindowSizeClassMediaQuery.viewInsetsOf(context).bottomPlatform.is*kIsWebdart:ioWindowSizeClasssizeOfpaddingOfviewInsetsOfviewPaddingOf.ofnavigation-and-routingSafeAreascripts/check_adaptive.shwidget-compositionnavigation-and-routingStatefulShellRouteflutter-performancesizeOfselect.ofaccessibility-as-codewidget-golden-and-a11y-testingstate-management-riverpodMediaQueryLayoutBuilderNavigationRailDisplayFeature