Flutter Scale Kit
A high-performance responsive design package for Flutter that helps you create adaptive UIs across different screen sizes with easy-to-use scaling utilities.
โจ Key Features
- ๐ง Intelligent Auto-Configuration: Automatically detects optimal scale limits based on device type, screen size, orientation, and aspect ratioโworks out of the box in 95% of cases
- ๐ฑ Universal Platform Support: Handles phones, tablets, desktop, and web with platform-specific optimizations and built-in device detection helpers
- ๐ Orientation-Aware Scaling: Adjusts scaling factors on rotation with per-orientation boosts and optional overrides
- ๐งฉ Full Breakpoint & Behaviour Control: Tune mobile/tablet/desktop thresholds, choose how desktop mimics other breakpoints, and steer scaling logic without writing custom detectors
- ๐ฏ Design Fidelity: Keeps UI proportional to design mockups via size enums (
SKSize), design tokens, and centralizedScaleKitDesignValues - โก High Performance: Cached calculations, flyweight factories, and threshold-based updates minimize rebuild overhead
- ๐ ๏ธ Developer-Friendly API: Familiar
.w,.h,.sp,.rSafeextensions plusSKithelpers, responsive builders, and ThemeData integration - ๐ค Font Intelligence: Language-aware font configuration with Google Fonts/custom families applied automatically across Text and themes
- ๐งช Runtime Controls & Tooling: Enable/disable scaling at runtime, integrate with
device_preview, and compare raw Flutter vs scaled behavior instantly - ๐ Comprehensive Docs & Examples: Live web demo, screenshots, and step-by-step guides covering quick start through advanced tuning
If this package helps you, please click "Like" on the pub.dev page โ it improves discoverability and ranking.
๐ Support this project: If you find Flutter Scale Kit useful, consider buying me a coffee to help me continue developing and maintaining open-source packages. Your support means a lot! ๐
๐ Table of Contents
Jump to any section:
๐ Getting Started
- ๐ง Intelligent Auto-Configuration
- โจ Key Features
- ๐ฆ Installation
- ๐ Getting Started
- Device Metrics Mixin
๐ Complete API Reference
๐ Core Concepts
๐จ Core APIs
- Extension Methods (.w, .h, .sp, .rSafe)
- Typography & Theme
- Layout & Container Helpers (SKit)
- Spacing Widgets & Const Helpers
๐ ๏ธ Configuration & Advanced
โ๏ธ Advanced Features
๐งช Optional Tools
๐ Reference
๐ค Community
Screenshots
Mobile
|
Tablet
|
Desktop
Autoscale and Enable/Disable Examples
Autoscale: Enabled
|
Autoscale: Disabled
|
Package Enabled (Scaling On)
|
Package Disabled (Raw Flutter)
|
| Use the settings (tune icon) in the example app to toggle autoscale and package enable/disable, then Save. | |
๐ง Intelligent Auto-Configuration
Scale Kit is SMART โ it automatically detects and configures optimal scaling for your app:
๐ฑ What It Auto-Detectsโ Device Type
โ Screen Properties
โ Special Cases
|
โ๏ธ What It Optimizes๐ฏ Scale Limits (automatic)
๐ Orientation Boosts
๐ก Design Adaptation
|
Thatโs allโno minScale, maxScale, or boost knobs required. The limits and boosts you saw above activate automatically so 95% of projects ship with zero manual tuning.
๐ก When to tweak manually? Only when you need tighter compliance (e.g., ยฑ5% variance for brand-critical screens) or a custom scaling feel. Jump to Understanding Scale Limits for recipes, and follow the Quick Start Guide for the full setup flow.
Installation
Add this to your package's pubspec.yaml file:
flutter:
sdk: flutter
dependencies:
flutter_scale_kit: ^1.5.2
Then run:
flutter pub get
๐ Getting Started
Flutter Scale Kit provides multiple ways to create responsive UIs. Whether you prefer extension methods like .w and .sp, helper widgets like SKit.roundedContainer(), or comprehensive text styling with SKit.textFull(), there's an API that fits your workflow.
๐ฏ Quick Migration: Migrating from an existing Flutter project? Our automatic scaling widgets are drop-in replacements for Flutter's built-in widgets! Use search & replace to switch:
ContainerโSKContainerTextโSKTextPaddingโSKPaddingTextFieldโSKTextFieldElevatedButtonโSKElevatedButtonSwitchโSKSwitch- And many more! See Optimized Layout Widgets for the complete list.
Your existing code will work exactly the same, but now with automatic responsive scaling applied! No need to refactor or change your code structureโjust swap the widget classes.
๐ก Keep using extensions: Even inside these widgets you can still write
width: 120,width: 120.w, orwidth: 0.5.swClamp(200, 360)โthe package detects extension outputs automatically so thereโs no double scaling.
All text-related APIs (extensions, SKit.text*, responsive themes) automatically apply your FontConfig when one is registeredโotherwise they fall back to Flutter's default fonts so you can adopt the system gradually.
Quick Setup (2 minutes)
Step 1: Configure sizes & fonts (optional but recommended)
Set up your design system values at app startup:
void main() {
// Configure sizes at app startup (before runApp)
setPaddingSizes(SizeValues.custom(xs: 4, sm: 8, md: 16, lg: 24, xl: 32, xxl: 48));
setMarginSizes(SizeValues.custom(xs: 2, sm: 4, md: 8, lg: 12, xl: 16, xxl: 24));
setRadiusSizes(SizeValues.custom(xs: 2, sm: 4, md: 8, lg: 12, xl: 16, xxl: 24));
setSpacingSizes(SizeValues.custom(xs: 4, sm: 8, md: 12, lg: 16, xl: 20, xxl: 24));
setTextSizes(TextSizeValues.custom(s14: 15, s16: 17, s18: 20, s24: 26));
// Set default values for methods without parameters
setDefaultPadding(16);
setDefaultMargin(8);
setDefaultRadius(12);
setDefaultSpacing(8);
setDefaultTextSize(14);
// Optional: apply FontConfig during app bootstrap
FontConfig.instance
..setDefaultFont(googleFont: GoogleFonts.inter)
..setLanguageFont(
const LanguageFontConfig(
languageCode: 'ja',
googleFont: GoogleFonts.notoSansJp,
),
)
..setLanguageGroupFont(
const LanguageGroupFontConfig(
languageCodes: ['ar', 'fa', 'ur'],
googleFont: GoogleFonts.almarai,
),
);
// Optionally cache the defaults for reuse (e.g., wrap in your own widget)
final defaults = SKitValues.defaults(); // returns padding/margin/radius/spacing with current config
debugPrint('Default padding: ${defaults.padding}');
runApp(const MyApp());
}
Note: If you don't configure sizes, default values will be used (xs=2, sm=4, md=8, lg=12, xl=16, xxl=24). FontConfig is optionalโsee Typography & Theme for details.
Step 2: Wrap your app
Drop ScaleKitBuilder above your root app and pass the design size you targeted in Figma/Sketch:
import 'package:flutter/material.dart';
import 'package:flutter_scale_kit/flutter_scale_kit.dart';
import 'package:google_fonts/google_fonts.dart'; // Import for FontConfig example
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
// ๐ Wrap your app so every route inherits responsive scaling
return ScaleKitBuilder(
designWidth: 375,
designHeight: 812,
designType: DeviceType.mobile,
// Optional: customize device breakpoints (defaults: 600 / 1200 / 1600 / 1920)
// breakpoints: const ScaleBreakpoints(
// mobileMaxWidth: 640,
// tabletMaxWidth: 1180,
// desktopMaxWidth: 1680,
// ),
// Optional: lock desktop/web platforms to desktop behaviour only
// lockDesktopPlatforms: true,
// Optional: pick tablet/mobile variants when locked
// lockDesktopAsTablet: true,
// lockDesktopAsMobile: true,
// Optional: control rebuild threshold (default: 0.05 for mobile/tablet, 0.0 for desktop/web)
// sizeChangeThreshold: 0.01, // 1% threshold
// sizeChangeThreshold: 0.0, // Rebuild on any change
child: MaterialApp(
title: 'My App',
// Optional: create a responsive theme once ScaleKitBuilder is in place
theme: ResponsiveThemeData.create(
context: context,
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: HomePage(),
),
);
}
}
-
Need to lock Scale Kit to desktop behaviour (even on smaller windows)? Pass
deviceTypeOverride: DeviceType.desktoporDeviceType.webwhen constructingScaleKitBuilder. -
Want that lock to happen automatically whenever you're on web/desktop? Set
lockDesktopPlatforms: trueso Scale Kit forces desktop handling only on those platforms, and uselockDesktopAsTablet/lockDesktopAsMobileto decide which breakpoint desktop should mimic when locked. -
Need fine-grained control over when rebuilds trigger? Use
sizeChangeThresholdto set the percentage change required before recalculating scales. Defaults to 5% for mobile/tablet and 0% (rebuild on any change) for desktop/web. Set to0.0to rebuild on any size change. -
Need RTL-aware spacing? Pass
start/endtocontext.scalePadding,context.scaleMargin, orSKit.paddingso Directionality resolves the correct edges automatically. -
Phones hold between 0.85โ1.25ร, even on foldables.
-
Tablets expand comfortably without blowing out typography.
-
Desktop & web respect tiny windows and ultrawide monitors.
๐ FontConfig & responsive theme are optional: if you never call
FontConfigorResponsiveThemeData, everything renders with Flutter's defaults. Configure them later for a complete design system (see Typography & Theme)โall SKit text helpers and cached styles will pick up your fonts automatically.
Need a different feel? Head to Understanding Scale Limits for tighter or looser ranges.
Core APIs - Pick Your Style
Once your app is wrapped with ScaleKitBuilder, you can start using Scale Kit in four ways:
๐ก New in v1.3.0: Use automatic scaling widgets (
SKContainer,SKPadding,SKMargin,SKText) that automatically apply scaling without needing extension methods! See Optimized Layout Widgets below.
Device Metrics Mixin
Need quick access to device helpers inside any State class without grabbing a BuildContext? Mix in DeviceMetricsMixin to get logical screen size, pixel ratio, and platform flags that stay in sync with WidgetsBinding.
class DashboardState extends State<Dashboard>
with DeviceMetricsMixin<Dashboard> {
@override
Widget build(BuildContext context) {
if (isDesktop) return const DesktopDashboard();
if (isTablet) return const TabletDashboard();
return const MobileDashboard();
}
@override
void onDeviceMetricsChanged(Size previous, Size current) {
debugPrint('Metrics changed: $current');
}
}
The mixin exposes logicalScreenSize, devicePixelRatio, isMobile, isTablet, isDesktop, and platform-specific helpers like isMobilePlatform, isDesktopPlatform, isWeb, isAndroidPlatform, and isIOSPlatform. Keep using your existing responsive logicโthis is just a lightweight shortcut when you need those values quickly.
1. Extension Methods (Quick & Familiar)
Container(
width: 200.w, // Scaled width
height: 100.h, // Scaled height
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.rSafe),
),
child: Text(
'Hello World',
style: TextStyle(fontSize: 16.sp), // Scaled font size
),
)
// Math constraint extensions (cached for performance)
Container(
width: 200.wMax(300), // Scaled width, max 300
height: 100.hClamp(50, 150), // Scaled height, clamped 50-150
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.rClamp(8, 20)), // Radius clamped 8-20
),
child: Text(
'Constrained Text',
style: TextStyle(fontSize: 16.spClamp(12, 24)), // Font size clamped 12-24
),
)
// Apply .w / .h / .r directly on insets, constraints, and radii
final padding = EdgeInsets.only(left: 12, right: 12).w; // EdgeInsets.only(left: 12.w, right: 12.w)
final rtlPadding = EdgeInsetsDirectional.only(start: 16).h; // Direction-aware, uses height scaling
final constraints = BoxConstraints(maxWidth: 200, minHeight: 80).r; // Radius-aware scaling
final radius = Radius.circular(24).w; // Radius.circular(24.w)
final borderRadius = BorderRadius.circular(16).h; // BorderRadius.circular(16.h)
2. Automatic Scaling Widgets (Zero Extension Methods Needed)
SKContainer(
width: 200, // Automatically scaled!
height: 100, // Automatically scaled!
padding: EdgeInsets.all(16), // Automatically scaled!
margin: EdgeInsets.all(8), // Automatically scaled!
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12), // Uses rSafe automatically!
color: Colors.blue.shade50,
),
child: SKText(
'Hello',
fontSize: 16, // Automatically scaled!
fontWeight: FontWeight.bold,
),
)
๐ก Easy Migration Tip: These widgets are drop-in replacements! Migrate your existing Flutter project by simply using search & replace:
ContainerโSKContainer,TextโSKText,PaddingโSKPadding,TextFieldโSKTextField,ElevatedButtonโSKElevatedButton, etc. Your existing code will work immediately with automatic scaling!
See Optimized Layout Widgets section for complete details.
3. SKit Helper Widgets (Pre-built & Convenient)
SKit.padding(
all: 16,
child: SKit.roundedContainer(
all: 12,
color: Colors.blue.shade50,
borderColor: Colors.blue,
borderWidth: 2,
child: Text('Hello'),
),
)
SKit.padding and SKit.margin also accept start / end so you can keep one layout definition for both RTL and LTR flows.
4. Comprehensive Text Widgets (All-in-One)
SKit.textFull(
'Styled Text',
fontSize: 18, // Automatically scaled
fontWeight: FontWeight.bold,
color: Colors.blue,
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
)
Note: Most containers need both borderRadius and border. Use borderColor and borderWidth parameters to add borders to your rounded containers. All border widths are automatically scaled based on screen size.
Popular Patterns & Building Blocks
Basic Responsive Layout
ScaleKitBuilder(
designWidth: 375,
designHeight: 812,
child: MaterialApp(
home: Scaffold(
body: Padding(
padding: EdgeInsets.all(20.w),
child: Text(
'Welcome!',
style: TextStyle(fontSize: 20.sp),
),
),
),
),
);
- Place
ScaleKitBuilderdirectly above yourMaterialApp(orCupertinoApp) so every route inherits scaling. .w,.h, and.spkeep spacing, components, and fonts proportional to the screen.
Responsive Containers & Cards
SKit.roundedContainer(
all: 16,
color: Colors.white,
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 16.h),
borderColor: Colors.black12,
borderWidth: 1,
radiusMode: SKRadiusMode.safe, // uses rSafe clamp internally
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SKit.text(
'Dashboard',
textSize: SKTextSize.s20,
fontWeight: FontWeight.w600,
),
SizedBox(height: 12.h),
SKit.text(
'All metrics auto-scale with the screen.',
textSize: SKTextSize.s16,
color: Colors.grey.shade600,
),
],
),
);
- One-liners for padding/margin:
SKit.padding(horizontal: 24, vertical: 12); use enums for presets (SKit.paddingSize(horizontal: SKSize.lg)). - Containers respect
.rSafeby default viaSKRadiusMode.safe, so borders stay natural on large screens.
Advanced Layout Switcher
SKResponsiveBuilder(
mobile: (_) => _CardsGrid(columns: 1),
tablet: (_) => _CardsGrid(columns: 2),
desktop: (_) => _CardsGrid(columns: 4),
mobileLandscape: (_) => _CardsGrid(columns: 2),
// Optional: force desktop/web into tablet/mobile breakpoints
// lockDesktopAsTablet: true,
// lockDesktopAsMobile: true,
// deviceTypeOverride: DeviceType.tablet,
);
- Supplies dedicated builders per device/orientation and falls back intelligently when one is missing.
- Perfect for dashboards, catalog pages, or any layout that needs different density on phones vs tablets vs desktop.
Responsive Int/Double Values
For simple responsive values like grid columns, spacing, or counts, use responsive integers and doubles:
// Responsive grid columns
final columns = SKit.responsiveInt(
context: context,
mobile: 2, // required base value
tablet: 4, // optional - falls back to mobile if null
desktop: 6, // optional - falls back to tablet โ mobile if null
mobileLandscape: 3, // optional override for mobile landscape
// Optional desktop controls (match builders):
// deviceTypeOverride: DeviceType.tablet,
// lockDesktopAsTablet: true,
// lockDesktopAsMobile: true,
// desktopAs: DesktopAs.tablet,
);
// Use in GridView
GridView.count(
crossAxisCount: columns,
children: [...],
)
// Responsive spacing values
final spacing = SKit.responsiveDouble(
context: context,
mobile: 8.0,
tablet: 16.0,
desktop: 24.0,
// Same optional controls as responsiveInt
// lockDesktopAsMobile: true,
);
// Responsive item counts
final maxItems = SKit.responsiveInt(
context: context,
mobile: 10,
tablet: 20,
desktop: 50,
);
- Define responsive values once and use them throughout your UI
- Automatic fallback: desktop โ tablet โ mobile
- Works with both integers and doubles
- Advanced options mirror the widget API:
deviceTypeOverrideto force a specific breakpointlockDesktopAsTablet/lockDesktopAsMobileto remap desktop/web when the lock is activedesktopAsto reuse tablet/mobile fallbacks without toggling the lock
- Pass the current
BuildContext(context: context) so values refresh automatically when the window resizes or rotates.
Design System Tokens
const design = ScaleKitDesignValues(
textMd: 16,
paddingMd: 16,
radiusMd: 12,
spacingMd: 16,
);
ScaleKitBuilder(
designWidth: 375,
designHeight: 812,
child: MaterialApp(
home: Builder(
builder: (context) {
final values = design.compute();
return SKPadding(
padding: values.paddingMd!,
child: SKContainer(
margin: values.marginMd,
decoration: BoxDecoration(
borderRadius: values.borderRadiusMd,
),
child: Text('Hello', style: values.textMd),
),
);
},
),
),
);
- Define tokens once, compute per screen, and reuse responsive values everywhere.
- Works great with const widgets and reduces repetitive size/spacing code.
๐ฅ๏ธ Desktop & Web Freedom
Building for desktop and the web is trickier than phones or tablets: users resize windows constantly, ultrawide monitors coexist with tiny browser panes, and some flows feel better when they borrow a tablet or even a mobile layout. Scale Kit exposes extra switches so you can decide how each screen behaves instead of getting locked into a single breakpoint.
-
Responsive routing on your terms
lockDesktopPlatforms,lockDesktopAsTablet, andlockDesktopAsMobilelet you pin desktop/web platforms to a specific breakpoint when you prefer the tablet or mobile version of a screen. When the lock is enabled, Scale Kit still watches window width and only remaps when it makes sense (e.g., small desktop windows fall back to mobile, large ones stay desktop). -
Per-widget overrides when a page needs special treatment
Widgets such asSKResponsive,SKResponsiveBuilder, and helpers likeSKit.responsiveIntaccept flags (deviceTypeOverride,desktopAs,lockDesktopAsTablet, etc.) so individual routes can deviate from the global configuration. Use them to keep a dense data table on desktop while forcing the settings screen to reuse the tablet layout. -
Platform-aware layouts inside each breakpoint
Device type is only half the storyโcontext.isDesktopPlatform,context.isMobilePlatform,context.isWebPlatform,context.isAndroidPlatform,context.isIOSPlatform, andScaleManager.platformCategorylet you branch on Android vs iOS vs Windows/macOS even when they share the same responsive width. Pair these helpers withdeviceTypeOverrideto deliver an iOS-specific sheet on tablets or a Windows-style toolbar on large monitors without duplicating your layout logic. -
Width-only decisions without committing to layout swaps
The context helpers (context.isDesktopSize,context.isDesktopAtLeastTablet, ...) andScaleManager.screenSizeClasslet you branch on pure size classes even after you lock the device type. This is ideal when you only want to tweak spacing or font scale for ultrawide monitors without switching the entire widget tree. -
Fallbacks that mirror CSS breakpoints
Desktop values fall back to tablet โ mobile automatically unless you supply explicit builders/values. Combined withdesktopAs, this gives you the same control you expect from CSS media queries while staying inside Flutter idioms.
SKResponsiveBuilder(
desktop: (_) => DesktopDashboard(),
tablet: (_) => TabletDashboard(),
mobile: (_) => MobileDashboard(),
// Force desktop/web to reuse the tablet experience on smaller windows
lockDesktopAsTablet: true,
// But keep the analytics page locked to desktop layout regardless of width
deviceTypeOverride: DeviceType.desktop,
);
Widget buildToolbar(BuildContext context) {
if (context.isAndroidPlatform) {
return const AndroidToolbar(); // Android-specific actions
}
if (context.isIOSPlatform) {
return const IOSToolbar(); // iOS-specific actions
}
if (context.isDesktopPlatform) {
return const DesktopToolbar(); // macOS/Windows/Linux shortcuts
}
if (context.isWebPlatform) {
return const WebToolbar(); // Browser-friendly controls
}
return const UniversalToolbar();
}
These options exist because the web is unpredictable: sometimes you want full desktop density, other times a compact tablet card list reads better. Scale Kit gives you the knobsโuse the global locks to define the defaults, then override per page or per value so every screen stays intentional.
๐ Complete API Reference
๐ฏ Quick Start Complete! Above, you learned the essentials: setup, core APIs, and popular patterns.
๐ Now: Deep Dive - Below you'll find comprehensive documentation for every method, advanced tuning options, and implementation details. Use this as your complete reference guide.
You stay in charge: Scale Kit lets you redefine breakpoints, decide how desktop behaves, and adjust orientation boosts or size-class fallbacks while the engine handles all the wiringโno need for custom detectors or extension glue.
๐ Core Concepts
Understanding Scale Limits (minScale & maxScale)
Scale limits are the guard rails that keep responsive layouts feeling familiar as screens grow or shrink. In nearly every project you can leave them unset and let Scale Kit pick the right range for each device type and orientation.
- Auto first: the defaults protect small phones from feeling cramped and keep large monitors from blowing up every pixel.
- Optional overrides: add limits only when you need a tighter (or looser) experience than the defaults provide.
ScaleKitBuilder(
designWidth: 375,
designHeight: 812,
minScale: 0.9, // Optional override
maxScale: 1.2, // Optional override
child: MaterialApp(home: HomePage()),
);
Need the exact formulas or override recipes? Jump to the Advanced Tuning Reference.
Understanding Orientation Boosts (Advanced)
Orientation boosts are gentle multipliers that run after scale limits to keep content readable when a device rotates. Smart defaults already cover the common cases, so you only tweak them for specialized layouts.
| Device Type | Portrait | Landscape | Why it matters |
|---|---|---|---|
| Mobile | 1.0ร | 1.2ร | Wider view gets a bit more breathing room |
| Tablet | 1.0ร | 1.2ร | Big screens gain spacing when horizontal |
| Desktop | 1.0ร | 1.0ร | Desktops rarely need extra boosts |
- Boosts are split for fonts and sizes so text can scale differently from containers.
- System text scaling (
.sp) is respected on top of these multipliers.
Curious about the math, per-device parameters, or real-world scenarios? See the Advanced Tuning Reference.
Extension Methods (.w, .h, .sp, .rSafe)
All extension methods work similar to flutter_screenutil:
// Width scaling
200.w // Scaled width
// Height scaling
100.h // Scaled height
// Font size scaling
16.sp // Scaled font size
// Border radius scaling
12.r // Full responsive radius (great for circles/avatars)
12.rSafe // Stable radius (clamped, default for corners)
12.rFixed // Constant radius (no scaling)
// Screen percentage
0.5.sw // 50% of screen width
0.25.sh // 25% of screen height
// Font size with system factor
16.spf // Scaled font size with system text scale factor
// Spacing helpers (return SizedBox for gaps)
12.horizontalSpace // SizedBox(width: 12.w)
16.verticalSpace // SizedBox(height: 16.h)
// Math constraint extensions (cached for performance)
200.wMax(300) // Scaled width, max 300
200.wMin(100) // Scaled width, min 100
200.wClamp(100, 300) // Scaled width, clamped between 100-300
100.hMax(150) // Scaled height, max 150
100.hMin(50) // Scaled height, min 50
100.hClamp(50, 150) // Scaled height, clamped between 50-150
0.5.swMax(200) // 50% screen width, max 200
0.5.swMin(100) // 50% screen width, min 100
0.5.swClamp(100, 200) // 50% screen width, clamped 100-200
0.3.shMax(150) // 30% screen height, max 150
0.3.shMin(80) // 30% screen height, min 80
0.3.shClamp(80, 150) // 30% screen height, clamped 80-150
12.rMax(20) // Scaled radius, max 20
12.rMin(8) // Scaled radius, min 8
12.rClamp(8, 20) // Scaled radius, clamped 8-20
16.spMax(24) // Scaled font size, max 24
16.spMin(12) // Scaled font size, min 12
16.spClamp(12, 24) // Scaled font size, clamped 12-24
โก Performance Note: All constraint extensions (
.wMax(),.hClamp(),.spClamp(), etc.) are cached just like the base extensions. This means calculations only happen once per unique value/constraint combination, and results are reused during UI rebuilds. The cache automatically clears when screen size or orientation changes, ensuring values stay accurate while minimizing redundant calculations.
๐จ Core APIs
Typography & Theme
Comprehensive Text Widgets
Problem: Manually creating Text widgets with all attributes is verbose and repetitive.
Solution: Use SKit.textFull() and SKit.textStyleFull() with ALL Flutter Text/TextStyle attributes pre-configured!
โ These helpers respect your
FontConfigautomatically (or default platform fonts when none is configured), so typography stays consistent across languages without extra work.
SKit.textFull() - Complete Text Widget
Instead of writing this:
Text(
'Hello World',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w600,
color: Colors.blue,
letterSpacing: 0.5,
decoration: TextDecoration.underline,
shadows: [Shadow(color: Colors.black26, offset: Offset(1, 1))],
),
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
softWrap: true,
textDirection: TextDirection.ltr,
)
Simply write this:
SKit.textFull(
'Hello World',
fontSize: 18, // Automatically scaled!
fontWeight: FontWeight.w600,
color: Colors.blue,
letterSpacing: 0.5,
decoration: TextDecoration.underline,
shadows: [Shadow(color: Colors.black26, offset: Offset(1, 1))],
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
softWrap: true,
textDirection: TextDirection.ltr,
)
Available Parameters (30+ attributes):
- Style: fontSize, fontWeight, fontStyle, color, backgroundColor, fontFamily, fontFamilyFallback
- Spacing: letterSpacing, wordSpacing, height
- Decoration: decoration, decorationColor, decorationStyle, decorationThickness
- Effects: shadows, foreground, background
- Layout: textAlign, textDirection, textBaseline, leadingDistribution
- Behavior: softWrap, overflow, maxLines, textScaler
- Accessibility: semanticsLabel, textWidthBasis, textHeightBehavior, selectionColor
- Advanced: locale, fontFeatures, fontVariations
SKit.textStyleFull() - Complete TextStyle
Create comprehensive TextStyles with all Flutter attributes:
final headerStyle = SKit.textStyleFull(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
backgroundColor: Colors.blue,
letterSpacing: 1.2,
wordSpacing: 2.0,
height: 1.5,
decoration: TextDecoration.none,
shadows: [
Shadow(color: Colors.black38, offset: Offset(2, 2), blurRadius: 4),
],
textBaseline: TextBaseline.alphabetic,
leadingDistribution: TextLeadingDistribution.even,
);
Text('Header', style: headerStyle)
Why use these?
โ
All attributes in one place - no need to remember which parameters go where
โ
Automatic scaling - fontSize automatically scaled with .sp
โ
Type-safe - all Flutter Text/TextStyle parameters available with autocomplete
โ
Less boilerplate - write less code, get more functionality
โ
Consistent styling - reuse styles easily across your app
When to use:
- Use
SKit.text()for simple text (basic styling) - Use
SKit.textFull()when you need advanced Text widget features - Use
SKit.textStyleFull()when you need reusable comprehensive styles
ThemeData Integration
Use responsive scaling in your theme:
ScaleKitBuilder(
designWidth: 375,
designHeight: 812,
designType: DeviceType.mobile,
child: MaterialApp(
theme: ResponsiveThemeData.create(
context: context,
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: HomePage(),
),
)
Font Configuration (Automatic Font Selection)
Configure fonts for different languages. All TextStyles automatically use the configured font for the current language:
import 'package:google_fonts/google_fonts.dart';
void main() {
// Configure font for specific language (optional)
// If not configured, Flutter's default font will be used
FontConfig.instance.setLanguageFont(
LanguageFontConfig(
languageCode: 'ar',
googleFont: GoogleFonts.almarai, // Pass GoogleFonts function
),
);
FontConfig.instance.setLanguageFont(
LanguageFontConfig(
languageCode: 'en',
googleFont: GoogleFonts.inter,
),
);
// Configure font for language group
FontConfig.instance.setLanguageGroupFont(
LanguageGroupFontConfig(
languageCodes: ['ar', 'fa', 'ur'],
googleFont: GoogleFonts.almarai,
),
);
// Set default font (used when no specific language config exists)
FontConfig.instance.setDefaultFont(
googleFont: GoogleFonts.inter,
);
runApp(const MyApp());
}
Usage:
Once configured, all TextStyles automatically use the configured font:
// Automatic font application - no manual configuration needed
Text('Hello', style: TextStyle(fontSize: 16.sp)) // โ
Uses FontConfig automatically
// Or via theme - all theme text styles get the font automatically
ResponsiveThemeData.create(
context: context,
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
)
Layout & Container Helpers (SKit)
The SKit class provides convenient methods for creating widgets:
// Padding
SKit.padding(all: 16, child: widget)
SKit.paddingSize(all: SKSize.md, child: widget)
final insets = SKit.paddingEdgeInsets(all: 16); // Scaled EdgeInsets
// Margin
SKit.margin(12, child: widget)
SKit.marginSize(all: SKSize.md, child: widget)
final marginInsets = SKit.marginEdgeInsetsSize(all: SKSize.md);
// Rounded container with border on all sides
SKit.roundedContainer(
all: 12,
color: Colors.blue.shade50,
borderColor: Colors.blue,
borderWidth: 2,
)
// Lock the radius to design value (no scaling)
SKit.roundedContainer(
all: 12,
radiusMode: SKRadiusMode.fixed,
color: Colors.orange.shade50,
)
// Force fully responsive radius (useful for pills/avatars)
SKit.roundedContainer(
all: 50,
radiusMode: SKRadiusMode.scaled,
color: Colors.purple.shade50,
)
> By default, all `SKit.rounded*` helpers use `SKRadiusMode.safe`, which gently clamps the radius to keep corners natural on large displays.
// Rounded container with border on specific sides
SKit.roundedContainer(
all: 12,
color: Colors.green.shade50,
borderTop: true,
borderBottom: true,
borderColor: Colors.green,
borderWidth: 2,
)
// Rounded container with different colors per side
SKit.roundedContainer(
all: 12,
color: Colors.pink.shade50,
borderTop: true,
borderTopColor: Colors.red,
borderTopWidth: 3,
borderBottom: true,
borderBottomColor: Colors.blue,
borderBottomWidth: 2,
)
// Rounded container with gradient, elevation, and image overlay
SKit.roundedContainer(
all: 16,
gradient: const LinearGradient(
colors: [Color(0xFF7F7FD5), Color(0xFF86A8E7), Color(0xFF91EAE4)],
),
elevation: 8,
shadowColor: Colors.black54,
backgroundImage: const DecorationImage(
image: AssetImage('assets/images/rounded_bg.png'),
fit: BoxFit.cover,
colorFilter: ColorFilter.mode(Colors.black26, BlendMode.darken),
),
padding: EdgeInsets.all(20),
child: Text(
'Gradient + shadow + background image',
style: TextStyle(color: Colors.white),
),
)
SKit.roundedContainerSize(
all: SKSize.md,
color: Colors.blue.shade50,
borderColor: Colors.blue,
borderWidth: 2,
)
// Fetch scaled EdgeInsets directly (great for custom widgets/layouts)
final padding = SKit.paddingEdgeInsetsSize(horizontal: SKSize.md, vertical: SKSize.sm);
final rawPadding = SKit.paddingEdgeInsets(all: 12); // accepts doubles when you skip enums
// Spacing
SKit.hSpace(8) // Horizontal spacing
SKit.vSpace(8) // Vertical spacing
SKit.sSpace(8) // Square spacing
Spacing Widgets & Const Helpers
Enjoy fluent numeric spacing like 20.horizontalSpace alongside dedicated const widgets when you need absolute control.
Const-friendly widgets
SKSizedBox({width, height})โ thin wrapper aroundSizedBoxfor const usage with precomputed valuesHSpace(double width)โ horizontal gapVSpace(double height)โ vertical gapSSpace(double size)โ square gap in both axes
Numeric extensions (scaled automatically)
12.horizontalSpaceโHSpace(12.w)16.verticalSpaceโVSpace(16.h)
These helpers are cached by the same ScaleValueFactory used by .w/.h, so they stay fast even when reused inside lists.
Row(
children: [
Container(width: 32.w, height: 32.w, color: Colors.purple),
12.horizontalSpace,
Container(width: 32.w, height: 32.w, color: Colors.green),
20.horizontalSpace,
Container(width: 32.w, height: 32.w, color: Colors.orange),
],
);
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(height: 12.h, color: Colors.blue),
12.verticalSpace,
Container(height: 12.h, color: Colors.pink),
16.verticalSpace,
Container(height: 12.h, color: Colors.teal),
],
);
Optimized Layout Widgets
Includes: SKPadding, SKMargin, SKContainer, SKText, SKIcon, SKCard, SKDivider, SKAppBar, SKListTile, SKSwitch, SKSwitchListTile, SKTextField, SKTextFormField, SKElevatedButton, SKTextButton, SKOutlinedButton, SKIconButton, SKActionChip, SKFilterChip, SKChoiceChip, SKInputChip, SKImage.
Minimize rebuild work with lightweight wrappers while keeping everything scaled automatically. These widgets automatically apply scaling to their properties without needing extension methods:
๐ Easy Migration from Existing Projects: Because these are drop-in replacements for Flutter's built-in widgets, you can easily migrate your existing project using simple search and replace operations! Just replace
ContainerโSKContainer,TextโSKText,PaddingโSKPadding,TextFieldโSKTextField,ElevatedButtonโSKElevatedButton, etc. All your existing code will work exactly the same, but now with automatic scaling applied!
SKPadding(
padding: EdgeInsets.all(16), // Automatically scaled!
child: SKMargin(
margin: EdgeInsets.only(bottom: 12), // Automatically scaled!
child: SKContainer(
width: 200, // Automatically scaled!
height: 100, // Automatically scaled!
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 10), // Automatically scaled!
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(12), // Uses rSafe automatically!
),
child: SKText(
'Scaled padding, margin, container, and text automatically',
fontSize: 14, // Automatically scaled!
fontWeight: FontWeight.bold,
),
),
),
)
// More automatic scaling widgets
SKIcon(Icons.home, size: 24) // Size automatically scaled!
SKCard(
margin: EdgeInsets.all(16), // Automatically scaled!
elevation: 4, // Automatically scaled!
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), // Uses rSafe automatically!
),
child: Text('Card content'),
)
SKDivider(
thickness: 1, // Automatically scaled!
indent: 16, // Automatically scaled!
endIndent: 16, // Automatically scaled!
)
SKAppBar(
title: Text('Title'),
toolbarHeight: 56, // Automatically scaled!
elevation: 4, // Automatically scaled!
titleSpacing: 16, // Automatically scaled!
)
SKListTile(
leading: SKIcon(Icons.person, size: 24),
title: SKText('Title', fontSize: 16),
contentPadding: EdgeInsets.all(16), // Automatically scaled!
minLeadingWidth: 40, // Automatically scaled!
)
Key Features:
SKContainer: Automatically scaleswidth,height,padding,margin,borderRadius(usingrSafe),boxShadow,borderwidth, andconstraintsSKPadding: Automatically scalesEdgeInsetsvalues (supports bothEdgeInsetsandEdgeInsetsDirectional)SKMargin: Automatically scalesEdgeInsetsvalues (supports bothEdgeInsetsandEdgeInsetsDirectional)SKText: Automatically scalesfontSizeand appliesFontConfigif configured (supports all Text widget properties)SKIcon: Automatically scalessizeSKCard: Automatically scalesmargin,elevation, andshape.borderRadiusSKDivider: Automatically scalesthickness,indent,endIndent, andheightSKAppBar: Automatically scalestoolbarHeight,elevation,titleSpacing, andleadingWidthSKListTile: Automatically scalescontentPadding,minLeadingWidth, andminVerticalPaddingSKSwitch: Automatically scalessplashRadiusSKSwitchListTile: Automatically scalescontentPaddingandsplashRadiusSKTextField: Automatically scalesfontSize,padding(viaInputDecoration),borderRadius, andcursorWidth/cursorHeightSKTextFormField: Same asSKTextFieldwith form validation supportSKElevatedButton: Automatically scalespadding,minimumSize,fixedSize,borderRadius, andelevationSKTextButton: Automatically scalespadding,minimumSize,fixedSize, andborderRadiusSKOutlinedButton: Automatically scalespadding,minimumSize,fixedSize, andborderRadiusSKIconButton: Automatically scalesiconSize,padding, andconstraintsSKActionChip: Automatically scalespadding,labelPadding,avatarPadding,borderRadius,elevation, anddeleteIconSizeSKFilterChip: Automatically scalespadding,labelPadding,avatarPadding,borderRadius,elevation, anddeleteIconSizeSKChoiceChip: Automatically scalespadding,labelPadding,avatarPadding,borderRadius, andelevationSKInputChip: Automatically scalespadding,labelPadding,avatarPadding,borderRadius,elevation, anddeleteIconSizeSKImage: Automatically scaleswidthandheight. Supports multiple image sources:SKImage.asset()- Load images from asset bundles (likeImage.asset)SKImage.network()- Load images from network URLs (likeImage.network)SKImage.file()- Load images from local files (likeImage.file)SKImage.memory()- Load images from in-memory byte data (likeImage.memory)
All constructors automatically scale width and height parameters and support cache optimization via cacheWidth and cacheHeight.
๐ก Mix raw doubles with
.w/.sw/.h/.r/.spseamlessly: every SK widget inspects incoming numbers and only scales when needed. That means you can passwidth: 120,width: 120.wMax(240), orborderRadius: BorderRadius.circular(24.r)to the same widget without double-scaling or swapping back to Flutter's base widgets.
Usage Examples:
// Asset image
SKImage.asset(
'assets/images/logo.png',
width: 100,
height: 100,
fit: BoxFit.cover,
)
// Network image
SKImage.network(
'https://example.com/image.jpg',
width: 200,
height: 200,
headers: {'Authorization': 'Bearer token'},
cacheWidth: 400, // Optimize memory usage
cacheHeight: 400,
)
// File image
SKImage.file(
File('/path/to/image.png'),
width: 150,
height: 150,
)
// Memory image
SKImage.memory(
imageBytes,
width: 120,
height: 120,
)
All scaling uses the package's cached scaling system for optimal performance.
๐ ๏ธ Configuration & Advanced
Size System Configuration
Important: Configure your size values at the start of your app (typically in main() or app initialization) before using size enums. This ensures consistent sizing throughout your application.
Where to Configure
Set up your size configurations in your app's initialization:
void main() {
// Configure sizes at app startup (before runApp)
setPaddingSizes(SizeValues.custom(xs: 4, sm: 8, md: 16, lg: 24, xl: 32, xxl: 48));
setMarginSizes(SizeValues.custom(xs: 2, sm: 4, md: 8, lg: 12, xl: 16, xxl: 24));
setRadiusSizes(SizeValues.custom(xs: 2, sm: 4, md: 8, lg: 12, xl: 16, xxl: 24));
setSpacingSizes(SizeValues.custom(xs: 4, sm: 8, md: 12, lg: 16, xl: 20, xxl: 24));
setTextSizes(TextSizeValues.custom(s14: 15, s16: 17, s18: 20, s24: 26));
// Set default values for methods without parameters
setDefaultPadding(16);
setDefaultMargin(8);
setDefaultRadius(12);
setDefaultSpacing(8);
setDefaultTextSize(14);
runApp(const MyApp());
}
Using Size Enums
After configuration, use size enums throughout your app:
// Padding with size enum
SKit.paddingSize(all: SKSize.md, child: widget)
SKit.paddingSize(horizontal: SKSize.lg, vertical: SKSize.sm, child: widget)
// Margin with size enum
SKit.marginSize(all: SKSize.md, child: widget)
// Radius with size enum
SKit.roundedContainerSize(all: SKSize.lg, color: Colors.blue)
// Spacing with size enum
SKit.hSpaceSize(SKSize.md) // Horizontal spacing
SKit.vSpaceSize(SKSize.sm) // Vertical spacing
Using Default Values
When you've set default values, you can use methods without parameters:
SKit.pad() // Uses default padding (16)
SKit.margin() // Uses default margin (8)
SKit.rounded() // Uses default safe radius (12)
SKit.h() // Uses default spacing (8)
SKit.v() // Uses default spacing (8)
Rounded Container with Border
Most containers need both border radius and border. Use borderColor and borderWidth parameters:
// Container with radius and border on all sides
SKit.roundedContainer(
all: 12,
color: Colors.blue.shade50,
borderColor: Colors.blue,
borderWidth: 2, // Border thickness (automatically scaled)
child: Text('Content'),
)
// Border on specific sides only
SKit.roundedContainer(
all: 12,
color: Colors.green.shade50,
borderTop: true, // Border on top
borderBottom: true, // Border on bottom
borderColor: Colors.green,
borderWidth: 2,
child: Text('Content'),
)
// Different colors and widths for different sides
SKit.roundedContainer(
all: 12,
color: Colors.pink.shade50,
borderTop: true,
borderTopColor: Colors.red,
borderTopWidth: 3,
borderBottom: true,
borderBottomColor: Colors.blue,
borderBottomWidth: 2,
borderLeft: true,
borderLeftColor: Colors.green,
borderLeftWidth: 1,
child: Text('Content'),
)
// Using size enum
SKit.roundedContainerSize(
all: SKSize.md,
color: Colors.blue.shade50,
borderColor: Colors.blue,
borderWidth: 2,
child: Text('Content'),
)
// Using default radius with border
SKit.rounded(
null,
const Text('Content'),
Colors.blue.shade50,
Colors.blue, // borderColor
2, // borderWidth
)
Border Parameters:
borderColor- Border color for all sides (if individual sides not specified)borderWidth- Border width for all sides (automatically scaled)borderTop,borderBottom,borderLeft,borderRight- Show border on specific sides (boolean)borderTopColor,borderBottomColor,borderLeftColor,borderRightColor- Individual side colorsborderTopWidth,borderBottomWidth,borderLeftWidth,borderRightWidth- Individual side widths (automatically scaled)
ScaleKitDesignValues - Centralized Design System
Define your design tokens once, compute everywhere:
// 1. Define your design system (can be const)
const design = ScaleKitDesignValues(
textSm: 12, textMd: 14, textLg: 16,
paddingSm: 8, paddingMd: 16, paddingLg: 24,
spacingSm: 8, spacingMd: 16, spacingLg: 24,
radiusSm: 6, radiusMd: 12, radiusLg: 16,
);
// 2. Compute once per build/screen
final values = design.compute();
// 3. Use everywhere - all values auto-scaled
SKPadding(
padding: values.paddingMd!,
child: SKContainer(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: values.borderRadiusMd,
),
child: Text('Hello', style: values.textMd),
),
)
Why ScaleKitDesignValues?
- Performance: One computation for all design tokens
- Consistency: Centralized design system with automatic scaling
- Clean code: No scattered size/spacing values throughout your app
- Type-safe: Compile-time verification of design tokens
Pattern: Define once โ Compute once โ Use everywhere
Pro tip: For simple cases, use SKitValues.compute(padding: 16, margin: 8, borderRadius: 12) instead.
// Define your design tokens once (can be const)
const design = ScaleKitDesignValues(
textSm: 12,
textMd: 14,
textLg: 16,
paddingSm: 8,
paddingMd: 16,
paddingLg: 24,
spacingSm: 8,
spacingMd: 16,
spacingLg: 24,
radiusSm: 6,
radiusMd: 12,
);
@override
Widget build(BuildContext context) {
// Compute once per build
final values = design.compute();
return ListView.separated(
padding: values.paddingHorizontal,
itemCount: 20,
separatorBuilder: (_, __) => SizedBox(height: values.spacingMd!),
itemBuilder: (context, index) {
return SKContainer(
margin: values.marginHorizontal,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: values.borderRadiusMd,
),
padding: values.paddingMd,
child: Row(
children: [
Container(
width: values.widthSm ?? 40,
height: values.heightSm ?? 40,
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: values.borderRadiusSm,
),
),
SizedBox(width: values.spacingMd!),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Title', style: values.textLg?.copyWith(fontWeight: FontWeight.bold)),
SizedBox(height: values.spacingSm!),
Text('Subtitle text', style: values.textSm),
],
),
),
],
),
);
},
);
}
Legacy compute helper (simple one-off values)
If you only need a few values in places where defining a full theme is overkill, you can use the legacy SKitValues.compute factory. This is maintained for convenience but ScaleKitDesignValues.compute() is recommended for larger UIs.
final v = SKitValues.compute(
padding: 16,
margin: 8,
borderRadius: 12,
width: 120,
height: 48,
fontSize: 16,
);
return SKPadding(
padding: v.padding,
child: SKContainer(
margin: v.margin,
decoration: BoxDecoration(borderRadius: v.borderRadius),
width: v.width,
height: v.height,
child: Text('Button', style: TextStyle(fontSize: v.fontSize)),
),
);
Context Extensions
Use context extensions for responsive scaling:
Container(
padding: context.scalePadding(start: 24, end: 12, vertical: 16),
margin: context.scaleMargin(all: 8),
decoration: BoxDecoration(
borderRadius: context.scaleBorderRadius(all: 12),
),
child: const Text('Content'),
)
// Device detection
if (context.isMobile) {
// Mobile layout
} else if (context.isTablet) {
// Tablet layout
}
ScaleManager Direct API
Access scale values directly:
final scale = ScaleManager.instance;
Container(
width: scale.getWidth(200),
height: scale.getHeight(100),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(scale.getRadius(12)),
),
child: Text(
'Hello',
style: TextStyle(fontSize: scale.getFontSize(16)),
),
)
start/end parameters resolve to the correct side automatically based on Directionality, so RTL layouts get the same responsive spacing without maintaining separate left/right values.
Helper Properties
Access device properties similar to flutter_screenutil:
final scaleKit = ScaleManager.instance;
// Device properties
double screenWidth = scaleKit.screenWidth;
double screenHeight = scaleKit.screenHeight;
double pixelRatio = scaleKit.pixelRatio;
double statusBarHeight = scaleKit.statusBarHeight;
double bottomBarHeight = scaleKit.bottomBarHeight;
double textScaleFactor = scaleKit.textScaleFactor;
double scaleWidth = scaleKit.scaleWidth;
double scaleHeight = scaleKit.scaleHeight;
Orientation orientation = scaleKit.orientation;
DeviceType deviceType = scaleKit.deviceType;
PlatformCategory platform = scaleKit.platformCategory;
DeviceSizeClass sizeClass = scaleKit.screenSizeClass;
deviceTyperespectsdeviceTypeOverrideandlockDesktopPlatforms.platformCategorysafely reports the underlying platform without usingdart:io, so it works on web too.deviceTypeFor(DeviceClassificationSource source)exposes the responsive, platform, or size classifications so you can sync with the new context helpers or custom logic.desktopLockFallbacktells you which breakpoint desktop/web will mimic when the lock is active (configurable globally or per widget).screenSizeClassreflects the currentDeviceSizeClasshelper (small mobile โ extra large desktop) derived from your configured breakpoints.
Custom Breakpoints & Size Classes
Tweak the width thresholds used for device detection (and the derived size
classes) by supplying ScaleBreakpoints to ScaleKitBuilder:
const customBreakpoints = ScaleBreakpoints(
mobileMaxWidth: 540,
tabletMaxWidth: 1100,
desktopMaxWidth: 1650,
largeDesktopMaxWidth: 2200,
);
return ScaleKitBuilder(
breakpoints: customBreakpoints,
designWidth: 375,
designHeight: 812,
child: MaterialApp(...),
);
ScaleManager.screenSizeClass and the new context helpers (see below) let you
branch on the expanded DeviceSizeClass enumโranging from smallMobile all the
way up to extraLargeDesktop.
Responsive Builder & Columns
Build different widgets per device/orientation with sensible fallbacks, and resolve responsive integers (e.g., Grid columns) quickly.
SKResponsive Widget
Use when you have separate builders for each device/orientation:
// Widget builder with separate builders
SKResponsive(
mobile: (_) => Text('Mobile portrait'),
mobileLandscape: (_) => Text('Mobile landscape'), // Falls back to mobile if null
tablet: (_) => Text('Tablet portrait'),
tabletLandscape: (_) => Text('Tablet landscape'), // Falls back to tablet -> mobileLandscape -> mobile
desktop: (_) => Text('Desktop'),
// Optional: force a device type (e.g., keep desktop UI on web regardless of width)
deviceTypeOverride: DeviceType.desktop,
// Optional: when desktop lock is active, force tablet/mobile branches
lockDesktopAsTablet: true,
)
Fallback rules:
mobileLandscapeโ falls back tomobileif nulltabletLandscapeโ falls back totabletโmobileLandscapeโmobileif null- Device: desktop โ tablet โ mobile; tablet โ mobile
SKResponsiveBuilder Widget
Supports two usage patterns:
Pattern 1: Builder with device/orientation info Use when you need access to device and orientation in your builder function:
SKResponsiveBuilder(
builder: (context, device, orientation) {
if (device == DeviceType.mobile && orientation == Orientation.landscape) {
return Text('Mobile Landscape');
}
if (device == DeviceType.tablet) {
return Text('Tablet');
}
return Text('Desktop or other');
},
desktopAs: DesktopAs.tablet, // Optional: make desktop behave like tablet
// Optional: force a specific device classification for this builder
deviceTypeOverride: DeviceType.desktop,
// Optional: push locked desktop to mobile/tablet branches
lockDesktopAsMobile: true,
)
Pattern 2: Separate builders (like SKResponsive) Use when you prefer separate builders for each device/orientation:
SKResponsiveBuilder(
mobile: (_) => Text('Mobile'),
tablet: (_) => Text('Tablet'),
desktop: (_) => Text('Desktop'),
mobileLandscape: (_) => Text('Mobile Landscape'), // Falls back to mobile if null
tabletLandscape: (_) => Text('Tablet Landscape'), // Falls back to tablet -> mobileLandscape -> mobile
)
Priority: If both patterns are provided, device-specific builders take priority over the main builder.
// Responsive integer with fallback rules (alias for columns)
final cols = SKit.responsiveInt(
context: context,
mobile: 2, // required base
tablet: 4, // optional (falls back to mobile if null)
desktop: 8, // optional (falls back to tablet->mobile if null)
mobileLandscape: 4, // optional override for mobile landscape
// tabletLandscape falls back to mobileLandscape, then tablet, then mobile
);
GridView.count(crossAxisCount: cols)
Both widgets support the same fallback rules:
- Device: desktop โ tablet โ mobile; tablet โ mobile
- Orientation: landscape โ device portrait; for tablet.landscape โ mobile.landscape โ mobile.portrait
- Desktop remapping: control it globally with
ScaleKitBuilder(lockDesktopPlatforms,lockDesktopAsTablet,lockDesktopAsMobile) or per widget/value via the same flags. UsedeviceTypeOverrideto bypass detection entirely when you need a hard switch. - Remapping respects live window size: e.g.
lockDesktopAsMobileonly routes desktop/web to the mobile branch when the window width falls under the mobile breakpoint; wider windows continue to render the desktop builder.
Desktop behavior (CSS-like):
- On Android/iOS, devices are classified only as mobile or tablet by width; desktop logic doesn't apply.
- On desktop (width โฅ 1200), desktop values are used by default. You can opt to reuse tablet/mobile values to mimic CSS breakpoints.
- This lets you build grids like in CSS (e.g., 2/4/8 columns) while forcing desktop to act like tablet/mobile if that's desired.
Examples:
// Make desktop behave like tablet for layout decisions
SKResponsive(
mobile: (_) => MobileView(),
tablet: (_) => TabletView(),
desktop: (_) => DesktopView(),
desktopAs: DesktopAs.tablet, // ๐ map desktop to tablet behavior
)
// Resolve an integer (e.g., Grid crossAxisCount) with desktop mapped to tablet
final cols = SKit.responsiveInt(
context: context,
mobile: 2,
tablet: 4,
desktop: 8,
desktopAs: DesktopAs.tablet, // ๐ desktop will use tablet values unless explicitly provided
);
GridView.count(crossAxisCount: cols)
Orientation Autoscale (Landscape vs Portrait)
Scale Kit lets you control autoscale behavior per orientation. Defaults are tuned for comfort: landscape boosts are enabled, portrait boosts are disabled.
ScaleKitBuilder(
designWidth: 375,
designHeight: 812,
// Orientation-specific flags
autoScaleLandscape: true, // default
autoScalePortrait: false, // default
// Optional landscape boosts
mobileLandscapeFontBoost: 1.2,
mobileLandscapeSizeBoost: 1.2,
)
Notes:
- Landscape: readability boosts (e.g., +20% fonts on mobile) can apply.
- Portrait: stable sizes by default; set
autoScalePortrait: trueif you want portrait boosts. - Size boosts only apply in landscape by default; portrait preserves your design intent.
API Reference
Extension Methods (on num)
.w- Scaled width (e.g.,200.w).sw- Screen width percentage (e.g.,0.5.sw= 50% width).sh- Screen height percentage (e.g.,0.25.sh= 25% height).rSafe- Stable radius with gentle clamping (e.g.,12.rSafe).r- Fully responsive radius/border radius (e.g.,12.r).rFixed- Constant radius (no scaling, e.g.,12.rFixed).sp- Scaled font size (e.g.,16.sp).h- Scaled height (e.g.,100.h).spf- Font size with system text scale factor (e.g.,16.spf)
Math Constraint Extensions (Cached):
.wMax(max),.wMin(min),.wClamp(min, max)- Width constraints.hMax(max),.hMin(min),.hClamp(min, max)- Height constraints.swMax(max),.swMin(min),.swClamp(min, max)- Screen width constraints.shMax(max),.shMin(min),.shClamp(min, max)- Screen height constraints.rMax(max),.rMin(min),.rClamp(min, max)- Radius constraints.spMax(max),.spMin(min),.spClamp(min, max)- Font size constraints- Works on
EdgeInsets,EdgeInsetsDirectional,BoxConstraints,Radius, andBorderRadius, so you can writeEdgeInsets.all(12).w,Radius.circular(16).h, orBorderRadius.circular(8).rwithout repeating conversions.
All constraint operations are cached to reduce calculations during UI rebuilds, only recalculating when the cache is cleared (on resize/orientation change).
Context Extensions
context.scaleWidth(double width)- Get scaled widthcontext.scaleHeight(double height)- Get scaled heightcontext.scaleFontSize(double fontSize)- Get scaled font sizecontext.scaleSize(double size)- Get scaled sizecontext.scalePadding(...)- Get responsive paddingcontext.scaleMargin(...)- Get responsive margincontext.scaleBorderRadius(...)- Get responsive border radiuscontext.isMobile- Check if device is mobilecontext.isTablet- Check if device is tabletcontext.isDesktop- Check if device is desktopcontext.isTypeOfMobile({source: DeviceClassificationSource.responsive})- Check mobile classification across responsive/platform/size sourcescontext.isTypeOfTablet({source: DeviceClassificationSource.responsive})- Check tablet classification across responsive/platform/size sourcescontext.isTypeOfDesktop({source: DeviceClassificationSource.responsive, includeWeb: true})- Check desktop classification, optionally treating web as desktopcontext.isDesktopPlatform- True when running on Windows/macOS/Linux/Webcontext.isWebPlatform- True when running on the webcontext.isMobilePlatform- True when running on Android/iOScontext.isAndroidPlatform- True when running on Androidcontext.isIOSPlatform- True when running on iOSDeviceClassificationSourcelets you target the responsive result (default), the raw platform classification, or width-only size classification when using the helpers above.context.screenSizeClass- Retrieve the currentDeviceSizeClasscontext.isSmallMobileSize/isMobileSize/isLargeMobileSize/isTabletSize/isLargeTabletSize/isDesktopSize/isLargeDesktopSize/isExtraLargeDesktopSize- Convenience checks for the expanded size classescontext.isDesktopMobileSize/isDesktopTabletSize/isDesktopDesktopOrLarger- Desktop/web-only size flags combining platform + size classcontext.isDesktopAtLeastTablet/isDesktopAtLeastDesktop- Desktop/web-only min-width helpers (use previous breakpoint thresholds, CSS-stylemin-width)context.scaleBreakpoints- Access the active breakpoint configuration
DeviceType Helpers
deviceType.isTypeOfMobile/.isTypeOfTablet/.isTypeOfDesktop/.isTypeOfWebexpose simple checks on the enum itself.
ScaleManager Properties
pixelRatio- Device pixel densityscreenWidth- Device width in logical pixelsscreenHeight- Device height in logical pixelsbottomBarHeight- Bottom safe zone distancestatusBarHeight- Status bar height (includes notch)textScaleFactor- System font scaling factorscaleWidth- Ratio of actual width to UI design widthscaleHeight- Ratio of actual height to UI design heightorientation- Screen orientation (portrait/landscape)devicePixelRatio- Physical pixels per logical pixeltopSafeHeight- Top safe area heightbottomSafeHeight- Bottom safe area heightsafeAreaHeight- Total safe area heightsafeAreaWidth- Safe area width
ScaleManager Methods
getWidth(double width)- Get scaled widthgetHeight(double height)- Get scaled heightgetFontSize(double fontSize)- Get scaled font sizegetFontSizeWithFactor(double fontSize)- Get scaled font size with system factorgetRadius(double radius)- Get scaled radiusgetScreenWidth(double percentage)- Get screen width percentagegetScreenHeight(double percentage)- Get screen height percentage
FontConfig API
FontConfig.instance- Singleton instance for font configurationsetLanguageFont(LanguageFontConfig)- Configure font for specific languagesetLanguageGroupFont(LanguageGroupFontConfig)- Configure font for language groupsetDefaultFont({googleFont?, customFontFamily?})- Set default fontsetLanguagesFonts(List<LanguageFontConfig>)- Configure multiple languages at oncesetLanguageGroupsFonts(List<LanguageGroupFontConfig>)- Configure multiple language groupsgetTextStyle({languageCode?, baseTextStyle})- Get TextStyle with configured fontcurrentLanguageCode- Get current language codeclear()- Clear all font configurations
Performance
Flutter Scale Kit uses intelligent caching to minimize recalculations:
- Flyweight Pattern: Reuses cached calculated values
- Cache Invalidation: Automatically clears cache on size/orientation change
- Const Widgets: Pre-calculated values for const-compatible widgets
- Singleton Pattern: Single instance manages all scaling operations
- Threshold-Based Updates: Only recalculates when size deltas exceed the active threshold (5% mobile/tablet, instantaneous for desktop/web unless overridden)
Architecture
The package uses design patterns for optimal performance:
- Singleton:
ScaleManager- Global scale configuration - Factory:
ScaleValueFactory- Creates cached scaled values - Flyweight:
ScaleValueCache- Reuses cached values
๐ ๏ธ Advanced Tuning Reference
Ready to go beyond the defaults? These notes expand on the core concepts above so you always know what will happen before you flip a switch.
Manual Scale Limit Overrides
Scale limits clamp the raw width/height ratios that Flutter Scale Kit calculates:
scaleWidth = screenWidth / designWidthscaleHeight = screenHeight / designHeightfinalScaleWidth = clamp(scaleWidth, minScale, maxScale)finalScaleHeight = clamp(scaleHeight, minScale, maxScale)
Real-world example
Design: 375ร812 (iPhone 13 mini) โ Displayed on an iPad Pro portrait (1024ร1366)
Without limits:
scaleWidth = 1024 / 375 = 2.73x โ 100px button โ 273px (too huge)
scaleHeight = 1366 / 812 = 1.68x
With minScale: 0.8, maxScale: 1.2:
clampedWidth = clamp(2.73, 0.8, 1.2) = 1.2x โ 100px button โ 120px โ
clampedHeight = clamp(1.68, 0.8, 1.2) = 1.2x
Override recipes
| Use Case | minScale | maxScale | Why |
|---|---|---|---|
| Strict design match | 0.95 |
1.05 |
Keeps UI locked to spec (brand/compliance) |
| Extra accessibility | 0.6 |
2.0 |
Lets system text scaling dominate |
| Locked tablet range | 0.9 |
1.2 |
Makes tablets feel like big phones |
| Desktop max limit | 0.7 |
1.3 |
Caps desktop scaling to avoid giant widgets |
Quick check
// Design: 375ร812, Screen: 1024ร1366, maxScale: 1.2
200.w; // 200 ร 1.2 = 240 (instead of 546 without clamping)
16.sp; // 16 ร 1.2 ร orientationBoost ร systemTextScale
Pro tip: Start with minScale โ 0.9 and maxScale โ 1.4 if you just want a gentle clamp for tablets while keeping phones untouched.
Orientation Boost Deep Dive
Orientation boosts multiply after clamping to adapt layouts when a device rotates:
finalSize = designValue ร clampedScale ร orientationSizeBoost
finalFontSize = designFontSize ร clampedScale ร orientationFontBoost ร systemTextScale
Smart defaults
| Device Type | Orientation | Font Boost | Size Boost | Why |
|---|---|---|---|---|
| Mobile | Portrait | 1.0ร | 1.0ร | Baseline |
| Mobile | Landscape | 1.2ร | 1.2ร | Landscape needs breathing room |
| Tablet | Portrait | 1.0ร | 1.0ร | Already spacious |
| Tablet | Landscape | 1.2ร | 1.2ร | Horizontal layouts feel airy |
| Desktop | Portrait | 1.0ร | 1.0ร | Rare use |
| Desktop | Landscape | 1.0ร | 1.0ร | Default desktop |
Math in action
Design: 375ร812 (mobile portrait) โ iPhone 14 landscape (852ร390)
Raw scales:
width = 852 / 375 = 2.27x
height = 390 / 812 = 0.48x
Auto limits applied (0.85 - 1.25):
width = clamp(2.27, 0.85, 1.25) = 1.25x
height = clamp(0.48, 0.85, 1.25) = 0.85x
Orientation boost (1.2ร landscape):
16.sp = 16 ร 1.25 ร 1.2 = 24.0px
100.w = 100 ร 1.25 ร 1.2 = 150.0px
50.h = 50 ร 0.85 ร 1.2 = 51.0px
Customization patterns
ScaleKitBuilder(
designWidth: 375,
designHeight: 812,
// Dashboardsโstay dense in landscape
mobileLandscapeFontBoost: 1.0,
mobileLandscapeSizeBoost: 1.0,
);
ScaleKitBuilder(
designWidth: 375,
designHeight: 812,
// Reading experienceโtext gets extra lift
mobileLandscapeFontBoost: 1.4,
mobileLandscapeSizeBoost: 1.1,
);
ScaleKitBuilder(
designWidth: 375,
designHeight: 812,
// Portrait-first tablet (POS/kiosk)
tabletPortraitFontBoost: 1.3,
tabletPortraitSizeBoost: 1.3,
);
Key takeaways
- Boosts run after clamping, so limits always win first.
- Fonts and sizes use separate multipliersโtune readability independently.
- System text scaling stacks on top of everything else.
๐งช Optional Tools
Enable/Disable Scaling (Runtime Toggle)
Keep a runtime switch handy when you want to compare Scale Kit against vanilla Flutter sizing.
final enabled = ValueNotifier<bool>(true);
ScaleKitBuilder(
designWidth: 375,
designHeight: 812,
enabledListenable: enabled, // runtime toggle
enabled: enabled.value, // initial
child: MaterialApp(...),
);
// Toggle anywhere
enabled.value = false; // disables scaling (values returned unmodified)
Notes:
.w/.h/.spand ScaleManager methods return raw values while disabled.- Reactivate to restore responsive scaling immediately.
- The example app's settings sheet (tune icon) exposes the same toggle for quick experiments.
Device Preview Integration (Optional)
If you use device_preview during development, share its simulated platform with Scale Kit so detection stays accurate inside the preview surface:
import 'package:device_preview/device_preview.dart';
void main() {
ScaleManager.setDevicePreviewPlatformGetter((context) {
try {
return DevicePreview.platformOf(context);
} catch (_) {
return null; // Fall back to default detection when preview is disabled
}
});
runApp(const MyApp());
}
Wrap your app with DevicePreview as normal (e.g., DevicePreview(builder: (_) => app)). Returning null keeps the default logic when preview mode is turned off. Skip this helper if you preferโthe package works fine without it; this just keeps platform detection perfect when you hop between simulated devices.
Device-Specific Scaling
The package automatically adapts scaling strategies based on:
- Device Type: Mobile, Tablet, Desktop, Web
- Aspect Ratio: Narrow, Wide, Standard
- Orientation: Portrait, Landscape
- Foldable Devices: Detects fold/unfold transitions
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
If you find this package useful, please consider:
- โญ Giving it a star on GitHub - it helps others discover the package
- ๐ Buy me a coffee - Support my open-source work and help me continue developing and maintaining packages
- ๐ Report bugs - Help improve the package by reporting issues
- ๐ก Suggest features - Share your ideas for improvements
- ๐ Share with others - Let other developers know about Flutter Scale Kit
Your support, whether through donations, stars, or feedback, helps me dedicate more time to:
- โจ Maintaining and improving Flutter Scale Kit
- ๐ Building new features and projects
- ๐ Creating better documentation and examples
- ๐ Fixing bugs and responding to issues faster
- ๐ก Exploring new ideas and innovations
Thank you for being part of this journey! Every contribution, no matter how small, makes a difference. ๐
Acknowledgements
Huge thanks to the authors and contributors of flutter_screenutil and similar responsive design packages. We used them extensively, learned from their great ideas, and built Flutter Scale Kit as an alternative optimized for our apps' performance and developer experience. flutter_screenutil is a solid package; this project simply explores a different set of tradeโoffs (compute-once patterns, caching, language-aware fonts, and orientation-aware scaling) that matched our needs.
FAQ
Q: Why choose Flutter Scale Kit over flutter_screenutil?
A: If you want compute-once patterns, automatic font selection by language, orientation-aware scaling controls, and an optional runtime toggle to compare raw Flutter vs scaled values, Scale Kit might fit better. If youโre happy with your current setup, flutter_screenutil remains an excellent choice.
Q: How do I disable scaling to compare with raw Flutter sizes?
A: Use ScaleKitBuilder(enabled: false) or provide enabledListenable for a runtime switch. In the example, tap the tune icon to toggle and Save.
Q: Can I control autoscale separately for portrait and landscape?
A: Yes. autoScaleLandscape (default true) and autoScalePortrait (default false) let you enable/disable boosts per orientation. You can also set device-specific font/size boosts.
Q: Do all TextStyles get my configured font automatically?
A: Yes. Fonts apply automatically via FontConfig integration in theme creation and text style scaling. If no configuration is provided, Flutterโs default font is used.
Q: Why is borderRadius removed when using different border colors per side?
A: Itโs a Flutter limitation. When individual sides have different colors, BoxDecoration canโt combine non-uniform borders with borderRadius. We avoid the rendering error by omitting borderRadius in those cases.
Q: Will this increase my package size?
A: The package only ships the lib/ code. The example and screenshots are not included in the pub.dev download. Use Google Fonts conditionally as needed.