Skip to content

Responsive design

Every utility class in Style can be applied conditionally at different breakpoints using responsive prefixes. This makes it easy to build adaptive interfaces without writing MediaQuery or LayoutBuilder code.


Use breakpoint prefixes like md: and lg: to apply utilities at specific screen widths:

Style(
'w-16 sm:w-32 lg:w-48 rounded-lg overflow-hidden',
child: Image.network('https://images.unsplash.com/photo-1554629947-334ff61d85dc?w=300&h=300&fit=crop'),
)

This works by applying w-16 (64px) by default, then overriding with w-32 (128px) at the sm breakpoint (640px) and w-48 (192px) at lg (1024px). Drag the preview handle to resize and see the breakpoints in action.

Style uses a mobile-first breakpoint system. Unprefixed utilities apply at all screen sizes, while prefixed utilities only apply at the specified breakpoint and above.

PrefixMin widthCSS equivalent
sm:640px@media (min-width: 640px)
md:768px@media (min-width: 768px)
lg:1024px@media (min-width: 1024px)
xl:1280px@media (min-width: 1280px)
2xl:1536px@media (min-width: 1536px)

A common pattern is a card that stacks vertically on mobile and switches to horizontal on larger screens:

class ResponsiveCardPage extends StatelessWidget {
const ResponsiveCardPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Style(
'mx-auto max-w-md overflow-hidden rounded-lg bg-white shadow-xl ring-1 ring-gray-900/5 sm:max-w-2xl',
child: Style(
'flex flex-col sm:flex-row',
children: [
Style(
'sm:shrink-0',
child: Style(
'h-[200px] w-full object-cover object-bottom sm:w-[193px] sm:object-center',
child: Image.network(
'https://images.unsplash.com/photo-1637734433731-621aca1c8cb6?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=404&q=80',
fit: BoxFit.cover,
),
),
),
Style(
'space-y-2 p-6 2xl:p-8',
children: [
Style(
'text-sm leading-6 font-medium text-indigo-600',
child: const Text('Company retreats'),
),
Style(
'block text-base leading-6 font-semibold text-gray-900 hover:underline',
child: const Text('Incredible accommodation for your team'),
),
Style(
'text-sm leading-6 text-gray-600',
child: const Text(
'Looking to take your team away on a retreat to enjoy awesome food and take in some sunshine? We have a list of places to do just that.',
),
),
],
),
],
),
),
);
}
}

Here’s what’s happening:

  • max-w-md constrains width on mobile, sm:max-w-2xl allows wider on small screens and up
  • flex flex-col stacks items vertically by default; sm:flex-row switches to horizontal at the sm breakpoint
  • h-[200px] w-full makes the image full-width on mobile; sm:w-[193px] gives it a fixed sidebar width on small screens and up
  • sm:shrink-0 prevents the image from shrinking in the flex layout

Since Style uses a mobile-first approach, don’t use sm: to target mobile — just use unprefixed utilities. Use sm: to override at the small breakpoint:

Style('text-center sm:text-left ...', child: Text('This text is centered on mobile and left-aligned on screens 640px and wider.'))
// DON'T use sm: to target mobile
Style('sm:text-center', child: ...) // This means centered at 640px+, not mobile
// DO use unprefixed for mobile, then override
Style('text-center sm:text-left', child: ...) // Centered by default, left-aligned at 640px+

Grids commonly change column count at different breakpoints:

Style('grid grid-cols-2 gap-4 sm:grid-cols-3 ...', children: [
Text('01'),
// ...
Text('06'),
])

Responsive prefixes can be combined with state variants like hover::

// On medium screens and above, change background on hover
Style('md:hover:bg-sky-100', child: ...)
// Dark mode + responsive
Style('dark:md:bg-gray-800', child: ...)

The order is always: responsive prefix first, then state variant, then utility.


BreakpointMin widthTypical devices
(default)0pxAll screens
sm:640pxLarge phones, small tablets
md:768pxTablets
lg:1024pxSmall laptops
xl:1280pxDesktops
2xl:1536pxLarge desktops