px_responsive
A powerful tri-tier responsive design system for Flutter that automatically scales your UI across mobile, tablet, and desktop platforms based on your Figma/XD design specifications.
Table of Contents
- Features
- Installation
- Quick Start
- How It Works
- API Reference
- Orientation Support
- Safe Area Awareness
- Responsive Widgets
- Platform Detection
- Configuration Options
- Best Practices
- Ultra-Wide Screen Support
- Complete Example
- WASM Support
- License
Features
- ๐ฏ Tri-Tier Scaling โ Automatically switches between mobile, tablet, and desktop base designs
- ๐ Design-to-Code Mapping โ Use exact values from your Figma/XD designs
- ๐ Safe Scaling โ Built-in min/max constraints prevent layout breaking
- ๐ฑ Device Detection โ Simple
isMobile,isTablet,isDesktopgetters andBuildContextextensions - ๐ Orientation Support โ
isLandscape/isPortraitgetters, orientation-specific base sizes,orientationValue<T>() - ๐ก๏ธ Safe Area Awareness โ
safeAreaTop,safeAreaBottom,safeScreenHeightpopulated automatically fromMediaQuery - ๐ฅ๏ธ Ultra-Wide Support โ Optional
maxWidthcap for large displays - ๐งฉ Rich Widget Library โ Responsive builders, visibility, padding, grid, animated transitions
- ๐ Debug Overlay โ
PxResponsiveDebugshows active breakpoint and scale factors at a glance - ๐ฅ๏ธ Platform Detection โ
PxPlatformTypeenum distinguishes Android, iOS, web, macOS, Windows, Linux - โจ Intuitive API โ Clean extension syntax (
.w,.h,.sp,.r,.verticalSpace,.horizontalSpace) - ๐ WASM Compatible โ Pure Dart implementation, works everywhere Flutter runs
Installation
Add px_responsive to your pubspec.yaml:
dependencies:
px_responsive: ^0.1.0
Then run:
flutter pub get
Quick Start
1. Wrap Your App
import 'package:flutter/material.dart';
import 'package:px_responsive/px_responsive.dart';
void main() {
runApp(
const PxResponsiveWrapper(
config: PxResponsiveConfig(
desktop: Size(1920, 1080),
tablet: Size(834, 1194),
mobile: Size(375, 812),
mobileBreakpoint: 600,
tabletBreakpoint: 1200,
),
child: MyApp(),
),
);
}
2. Use Extensions in Your Widgets
Container(
width: 200.w,
height: 150.h,
padding: EdgeInsets.all(16.r),
child: Text(
'Hello World',
style: TextStyle(fontSize: 18.sp),
),
)
3. Use Spacing Shorthands
Column(
children: [
Text('Title'),
16.verticalSpace, // SizedBox(height: 16.h)
Text('Body'),
],
)
4. Adapt to Device Types
// Global getters
if (isMobile) return MobileLayout();
// BuildContext extensions
if (context.isDesktop) return DesktopLayout();
// Responsive builder widget
PxResponsiveBuilder(
mobile: (_) => MobileLayout(),
tablet: (_) => TabletLayout(),
desktop: (_) => DesktopLayout(),
)
How It Works
The Scaling Formula
result = value ร (currentScreenWidth รท activeBaseDesignWidth)
Automatic Base Switching
| Screen Width | Active Base | Typical Device |
|---|---|---|
| < 600 px | Mobile (375 ร 812) | Phones |
| 600 โ 1199 px | Tablet (834 ร 1194) | Tablets, small laptops |
| โฅ 1200 px | Desktop (1920 ร 1080) | Desktops, large screens |
API Reference
Core Extensions
| Extension | Description | Use Case |
|---|---|---|
.w |
Width scaling | Container widths, horizontal padding |
.h |
Height scaling | Container heights, vertical padding |
.sp |
Font scaling (tighter max) | Text sizes |
.r |
Radius scaling (min of w & h) | Border radius, circular elements |
Container(
width: 300.w,
height: 200.h,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.r),
),
child: Text('Hello', style: TextStyle(fontSize: 16.sp)),
)
Screen Percentage Extensions
| Extension | Description |
|---|---|
.wf |
Percentage of effective screen width |
.hf |
Percentage of screen height |
Container(width: 80.wf, height: 50.hf)
Parent-Relative Extensions
Wrap the parent with PxRelativeSizeProvider to enable:
| Extension | Description |
|---|---|
.wr(context) |
Percentage of parent width |
.hr(context) |
Percentage of parent height |
PxRelativeSizeProvider(
child: Row(
children: [
Container(width: 30.wr(context), child: Sidebar()),
Container(width: 70.wr(context), child: MainContent()),
],
),
)
Clamping Methods
| Method | Description |
|---|---|
.wMin(min) / .wMax(max) / .wClamp(min, max) |
Width with bounds |
.hMin(min) / .hMax(max) / .hClamp(min, max) |
Height with bounds |
.spMin(min) / .spMax(max) / .spClamp(min, max) |
Font with bounds |
.rMin(min) / .rMax(max) / .rClamp(min, max) |
Radius with bounds |
width: 200.wClamp(150, 300), // Between 150 and 300
fontSize: 14.spMin(12), // At least 12
borderRadius: 8.rMax(16), // At most 16
Spacing Shorthands
Column(
children: [
Text('Section Title'),
24.verticalSpace, // SizedBox(height: 24.h)
Row(
children: [
Icon(Icons.home),
8.horizontalSpace, // SizedBox(width: 8.w)
Text('Home'),
],
),
],
)
Object Extensions
EdgeInsets
| Extension | Description |
|---|---|
.w |
All sides ร scaleW |
.scaled |
Horizontal ร scaleW, vertical ร scaleH |
.r |
All sides ร scaleR |
Size
| Extension | Description |
|---|---|
.scaled |
Width ร scaleW, height ร scaleH |
.w |
Both dimensions ร scaleW |
.r |
Both dimensions ร scaleR |
BorderRadius
| Extension | Description |
|---|---|
.r |
All corners ร scaleR |
TextStyle & Icon Extensions
Scale text styles and icons in one call:
// TextStyle โ scales fontSize, letterSpacing, wordSpacing
Text(
'Hello',
style: TextStyle(fontSize: 16, letterSpacing: 0.5).responsive,
)
// Icon โ scales size by scaleSp
Icon(Icons.home).responsive
// Or with a custom base size:
Icon(Icons.star, size: 32).responsive
Global Getters
| Getter | Type | Description |
|---|---|---|
isMobile |
bool |
True if width < mobileBreakpoint |
isTablet |
bool |
True if between breakpoints |
isDesktop |
bool |
True if width โฅ tabletBreakpoint |
deviceType |
PxDeviceType |
.mobile / .tablet / .desktop |
screenWidth |
double |
Actual screen width |
screenHeight |
double |
Actual screen height |
effectiveWidth |
double |
Width used for scaling (respects maxWidth) |
isLandscape |
bool |
True if width > height |
isPortrait |
bool |
True if height โฅ width |
orientation |
PxOrientation |
.portrait / .landscape |
Global Functions
// Device-type value picker
int columns = responsiveValue(mobile: 1, tablet: 2, desktop: 4);
// Orientation value picker
double padding = orientationValue(portrait: 16.0, landscape: 24.0);
BuildContext Extensions
Access responsive information directly from any BuildContext:
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(context.isMobile ? 12.w : 24.w),
child: Text(
'${context.deviceType.name} โ '
'${context.screenWidth.toStringAsFixed(0)} ร '
'${context.screenHeight.toStringAsFixed(0)}',
),
);
}
| Extension | Type |
|---|---|
context.responsive |
PxResponsive |
context.isMobile |
bool |
context.isTablet |
bool |
context.isDesktop |
bool |
context.deviceType |
PxDeviceType |
context.screenWidth |
double |
context.screenHeight |
double |
context.isLandscape |
bool |
context.isPortrait |
bool |
Orientation Support
Detect and react to device rotation anywhere in your widget tree.
// Global getters
if (isLandscape) return LandscapeLayout();
// Global function
double padding = orientationValue(portrait: 16.0, landscape: 24.0);
// PxResponsive instance
PxOrientation current = PxResponsive().orientation;
// BuildContext
if (context.isPortrait) return PortraitCard();
Landscape-Specific Design Sizes
Provide alternate base sizes for landscape so scaling matches your landscape Figma frames:
PxResponsiveWrapper(
config: const PxResponsiveConfig(
mobile: Size(375, 812),
mobileLandscape: Size(812, 375), // used when phone rotates
tablet: Size(834, 1194),
tabletLandscape: Size(1194, 834),
),
child: const MyApp(),
)
Safe Area Awareness
Access system UI insets without calling MediaQuery manually:
final r = PxResponsive();
print(r.safeAreaTop); // status bar / notch
print(r.safeAreaBottom); // home indicator
print(r.safeScreenHeight); // screenHeight - top - bottom
// Safe padding is captured automatically by PxResponsiveWrapper.
// For explicit control, pass it in init():
PxResponsive().init(
constraints: constraints,
config: config,
safeAreaPadding: MediaQuery.paddingOf(context),
);
Responsive Widgets
PxResponsiveWrapper
Initialises the responsive singleton using LayoutBuilder. Place it above MaterialApp/CupertinoApp.
PxResponsiveWrapper(
config: const PxResponsiveConfig(maxWidth: 1920),
child: const MyApp(),
)
PxResponsiveMediaQueryWrapper
Alternative wrapper that reads screen size from MediaQuery instead of LayoutBuilder. Use when the wrapper is inside a constrained subtree (e.g., a Dialog).
PxResponsiveMediaQueryWrapper(
config: const PxResponsiveConfig(),
child: const MyWidget(),
)
PxResponsiveBuilder
Build completely different widget trees per device type.
PxResponsiveBuilder(
mobile: (_) => const MobileLayout(),
tablet: (_) => const TabletLayout(),
desktop: (_) => const DesktopLayout(),
)
Fallback chain: desktop โ tablet โ mobile (if a builder is omitted).
PxResponsiveValue
Provide different typed values and build with them.
PxResponsiveValue<int>(
mobile: 1,
tablet: 2,
desktop: 4,
builder: (context, columns) => GridView.count(
crossAxisCount: columns,
children: items,
),
)
PxResponsiveVisibility
Show or hide widgets based on device type.
// Only on desktop
PxResponsiveVisibility.desktop(child: Sidebar())
// Mobile and tablet
PxResponsiveVisibility.tabletDown(child: CompactHeader())
// With replacement and state preservation
PxResponsiveVisibility.tabletUp(
replacement: const MenuButton(),
maintainState: true,
child: SideNavigation(),
)
| Constructor | Mobile | Tablet | Desktop |
|---|---|---|---|
.mobile() |
โ | โ | โ |
.tablet() |
โ | โ | โ |
.desktop() |
โ | โ | โ |
.tabletUp() |
โ | โ | โ |
.tabletDown() |
โ | โ | โ |
PxResponsivePadding
Apply device-specific padding with optional auto-scaling.
PxResponsivePadding(
mobile: const EdgeInsets.all(12),
tablet: const EdgeInsets.all(20),
desktop: const EdgeInsets.all(32),
scale: true, // additionally applies .scaled
child: const MyContent(),
)
PxResponsiveGrid
A GridView with automatic column count selection.
PxResponsiveGrid(
mobileColumns: 1,
tabletColumns: 2,
desktopColumns: 4,
spacing: 16.r,
childAspectRatio: 3 / 4,
children: productCards,
)
| Parameter | Description |
|---|---|
mobileColumns |
Columns on mobile (required) |
tabletColumns |
Columns on tablet (falls back to mobile) |
desktopColumns |
Columns on desktop (falls back to tablet) |
spacing |
Cross-axis spacing |
runSpacing |
Main-axis spacing (defaults to spacing) |
childAspectRatio |
Width / height ratio of each item |
shrinkWrap |
Set true inside non-scrollable parents |
AnimatedPxResponsiveBuilder
Smoothly transitions between layouts when the device type changes (e.g., desktop window resize).
AnimatedPxResponsiveBuilder(
duration: const Duration(milliseconds: 300),
switchInCurve: Curves.easeOut,
mobile: (_) => const MobileLayout(),
tablet: (_) => const TabletLayout(),
desktop: (_) => const DesktopLayout(),
)
Uses AnimatedSwitcher with FadeTransition by default. Pass a custom transitionBuilder for other effects.
PxResponsiveDebug
Overlay that shows current breakpoint, scale factors, and orientation during development.
PxResponsiveWrapper(
config: config,
child: PxResponsiveDebug(
enabled: kDebugMode, // remove in release
child: const MyApp(),
),
)
The overlay is positioned top-right and shows:
device : mobile
screen : 375ร812
orient : portrait
base : 375ร812
scaleW : 1.000
scaleH : 1.000
scaleSp: 1.000
scaleR : 1.000
effW : 375
Platform Detection
Detect the underlying OS independently of screen width:
import 'package:px_responsive/px_responsive.dart';
// Top-level getters
if (isNativeMobile) requestCameraPermission();
if (isNativeDesktop) showMenuBar();
if (isPlatformWeb) openBrowserLink(url);
// Full enum
switch (platformType) {
case PxPlatformType.ios:
return const CupertinoButton(child: Text('OK'), onPressed: null);
case PxPlatformType.android:
return ElevatedButton(onPressed: null, child: const Text('OK'));
default:
return TextButton(onPressed: null, child: const Text('OK'));
}
| Getter | Description |
|---|---|
platformType |
PxPlatformType enum value |
isNativeMobile |
true for Android or iOS |
isNativeDesktop |
true for macOS, Windows, or Linux |
isPlatformWeb |
true when running in a browser |
Configuration Options
PxResponsiveConfig
const PxResponsiveConfig({
// Portrait design sizes (required baselines)
Size desktop = const Size(1920, 1080),
Size tablet = const Size(834, 1194),
Size mobile = const Size(375, 812),
// Landscape design sizes (optional โ activates on rotation)
Size? desktopLandscape,
Size? tabletLandscape,
Size? mobileLandscape,
// Breakpoints
double mobileBreakpoint = 600, // below this โ mobile
double tabletBreakpoint = 1200, // above this โ desktop
// Ultra-wide cap
double? maxWidth, // null = no cap
// Scaling constraints
double? minScaleFactor = 0.5,
double? maxScaleFactor = 2.0,
double? maxTextScaleFactor = 1.5,
})
Common Configurations
Standard Setup
const PxResponsiveConfig(
desktop: Size(1440, 900),
tablet: Size(768, 1024),
mobile: Size(375, 812),
)
With Landscape Support
const PxResponsiveConfig(
mobile: Size(375, 812),
mobileLandscape: Size(812, 375),
tablet: Size(834, 1194),
tabletLandscape: Size(1194, 834),
)
With Ultra-Wide Cap
const PxResponsiveConfig(
desktop: Size(1920, 1080),
maxWidth: 1920,
)
Conservative Scaling
const PxResponsiveConfig(
minScaleFactor: 0.8,
maxScaleFactor: 1.5,
maxTextScaleFactor: 1.2,
)
Best Practices
1. Match Your Design Tool
// If your Figma mobile frame is 390ร844
mobile: Size(390, 844),
desktop: Size(1440, 900),
2. Use the Right Extension
| Scenario | Extension |
|---|---|
| Container width | .w |
| Container height | .h |
| Font size | .sp |
| Border radius / circles | .r |
| Vertical gap | .verticalSpace |
| Horizontal gap | .horizontalSpace |
3. Clamp Critical Values
height: 48.hMin(44), // Always tappable
fontSize: 14.spMin(12), // Always readable
borderRadius: 8.rClamp(4, 16), // Stays proportional
4. Use the Debug Overlay During Development
PxResponsiveDebug(
enabled: kDebugMode,
child: MyApp(),
)
Ultra-Wide Screen Support
Use maxWidth to prevent UI from stretching on 4K/ultrawide monitors:
Without maxWidth: 3840 px โ scale 2.0 ร โ 200 px button becomes 400 px
With maxWidth 1920: 3840 px โ effective 1920 px โ scale 1.0 ร โ 200 px
PxResponsiveWrapper(
config: const PxResponsiveConfig(
desktop: Size(1920, 1080),
maxWidth: 1920,
),
child: MyApp(),
)
Complete Example
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:px_responsive/px_responsive.dart';
void main() {
runApp(
const PxResponsiveWrapper(
config: PxResponsiveConfig(
mobileLandscape: Size(812, 375),
maxWidth: 1920,
),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: PxResponsiveDebug(
enabled: kDebugMode,
child: const HomePage(),
),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My App', style: TextStyle(fontSize: 20.sp)),
leading: PxResponsiveVisibility.mobile(
child: IconButton(
icon: const Icon(Icons.menu),
onPressed: () {},
),
),
),
body: Row(
children: [
PxResponsiveVisibility.desktop(
child: Container(
width: 240.w,
color: Colors.grey[100],
child: const Center(child: Text('Sidebar')),
),
),
Expanded(
child: PxResponsivePadding(
mobile: const EdgeInsets.all(12),
desktop: const EdgeInsets.all(32),
scale: true,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Hello, ${context.deviceType.name}!',
style: TextStyle(fontSize: 24.sp, fontWeight: FontWeight.bold),
),
16.verticalSpace,
Text(
orientationValue(
portrait: 'Portrait mode',
landscape: 'Landscape mode',
),
style: TextStyle(fontSize: 14.sp),
),
16.verticalSpace,
PxResponsiveGrid(
mobileColumns: 2,
tabletColumns: 3,
desktopColumns: 4,
spacing: 12.r,
shrinkWrap: true,
children: List.generate(
8,
(i) => Card(
child: Center(
child: Text('Item $i', style: TextStyle(fontSize: 14.sp)),
),
),
),
),
],
),
),
),
],
),
bottomNavigationBar: PxResponsiveVisibility.mobile(
child: BottomNavigationBar(
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Search'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
],
),
),
);
}
}
WASM Support
This package is fully compatible with Flutter's WebAssembly (WASM) compilation target. It uses only pure Dart code and standard Flutter widgets with no platform-specific plugins.
flutter build web --wasm
License
This project is licensed under the MIT License โ see the LICENSE file for details.
๐ค About the Author
๐ฅ Contributors
We appreciate all contributions to this project!
Support
If you find this package helpful, please give it a โญ on GitHub!
For bugs or feature requests, please open an issue.
Libraries
- px_responsive
- A powerful tri-tier responsive design system for Flutter.