responsive_metrics

A lightweight, dependency-free responsive design toolkit for Flutter.

No context needed for pixel scaling, simple breakpoint checks for layout decisions, and a small RV helper for values that genuinely differ per device class.

Install

dependencies:
  responsive_metrics: ^0.0.3

Setup

Initialize ScreenScaler once, inside your MaterialApp builder, so it stays up to date across window resizes:

import 'package:responsive_metrics/responsive_metrics.dart';

MaterialApp(
  builder: (context, child) {
    ScreenScaler.instance.init(MediaQuery.sizeOf(context));
    return child!;
  },
  home: const HomePage(),
);

Turning scaling off

Some apps target known, fixed hardware (a POS app on a specific tablet, say) and want zero proportional scaling — just fixed values plus RV/ pick() breakpoint-tier values. Pass scalingEnabled: false to make .w .h .sp .r return raw, unscaled values everywhere, mobile included:

ScreenScaler.instance.init(
  MediaQuery.sizeOf(context),
  scalingEnabled: false,
);

Or keep mobile scaling (where screen size varies the most) but turn it off just for tablet/desktop, where a fixed layout is usually the better fit:

ScreenScaler.instance.init(
  MediaQuery.sizeOf(context),
  scaleTablet: false,
  scaleDesktop: false,
);

All three flags default to true — existing scaling behavior — so nothing changes unless you opt out. They only need to be passed once (e.g. alongside your other startup config); omit them on later resize-triggered init() calls to keep whatever was configured.

Usage

1. Pixel scaling — .w .h .sp .r

Use these for any concrete pixel value. No BuildContext required.

Extension Use for
.w width, horizontal padding, margin, icon size
.h height, vertical padding, vertical spacing
.sp fontSize only
.r BorderRadius, circular radius
SizedBox(width: 24.w, height: 16.h)
Padding(padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h))
Icon(Icons.home, size: 24.w)
Text('Hello', style: TextStyle(fontSize: 16.sp))
BorderRadius.circular(12.r)

2. Breakpoint checks — context.isMobile / isTablet / isDesktop

Use these for layout decisions, not pixel values.

if (context.isDesktop) const SideNavBar(),
if (context.isMobile) const BottomNavBar(),

Flex(
  direction: context.isDesktop ? Axis.horizontal : Axis.vertical,
  children: [...],
)

3. context.pick() — different values per device class

GridView(
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: context.pick(mobile: 2, tablet: 3, desktop: 4),
  ),
)

4. RV — reusable, per-device-class constants

Use RV when a design value is genuinely different per breakpoint — not just scaled. If it's just scaling, use .w/.sp/.r directly instead.

class AppFontSize {
  static const headerTitle = 22.0; // scales normally, no RV needed
  static const cancelHeaderTitle = RV(mobile: 22.0, tablet: 30.0);
}

Text(
  'Cancel',
  style: TextStyle(fontSize: AppFontSize.cancelHeaderTitle.sp(context)),
)

RV exposes .value(context), .w(context), .h(context), .r(context), .sp(context), and .size(context).

A slightly larger example — a card whose padding is deliberately roomier on tablet/desktop, not just scaled up:

class AppSpacing {
  static const cardPadding = RV(mobile: 12.0, tablet: 20.0, desktop: 28.0);
}

Container(
  padding: EdgeInsets.all(AppSpacing.cardPadding.w(context)),
  child: Text(
    'Card title',
    style: TextStyle(fontSize: AppFontSize.cardTitle.sp(context)),
  ),
)

See example/lib/main.dart (the "See RV example" button) for a full runnable version of this.

Breakpoints

Matches Material Design 3:

  • Mobile → width < 600
  • Tablet → width >= 600 && width < 1200
  • Desktop → width >= 1200

Rule of thumb

  • Pixel size / spacing / font / radius → .w .h .sp .r
  • Layout decision (show/hide, columns, direction) → context.isX / context.pick()
  • A value that's genuinely different (not scaled) per breakpoint → RV

Libraries

responsive_metrics
A lightweight, dependency-free responsive design toolkit for Flutter.