adaptive_responsive_ui 1.0.5
adaptive_responsive_ui: ^1.0.5 copied to clipboard
A comprehensive Flutter package for responsive UI design with easy-to-use extensions for width, height, padding, margin, fonts, and device detection.
Adaptive Responsive UI #
A comprehensive Flutter package for building responsive UIs with easy-to-use extensions. Scale your UI elements based on design dimensions from Figma, Adobe XD, or any design tool.
✨ NEW: Context-less extensions! Initialize once, use everywhere without passing context!
Features #
- 🎯 Easy Responsive Sizing - Scale width, height, fonts, icons, and radius
- ⚡ Context-less Extensions - Initialize once, use everywhere without context!
- 📱 Device Detection - Identify mobile, tablet, and desktop devices
- 🎨 Smart Padding & Margin - Responsive spacing helpers
- 🔧 Configurable Base Dimensions - Match your exact design file dimensions
- 📏 Safe Area Helpers - Easy access to safe area insets
- 🎭 Responsive Widgets - Built-in responsive builder widget
Installation #
Add this to your package's pubspec.yaml file:
dependencies:
adaptive_responsive_ui: ^1.0.5
Then run:
flutter pub get
Quick Start #
Initialize once in your app #
import 'package:flutter/material.dart';
import 'package:adaptive_responsive_ui/adaptive_responsive_ui.dart';
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Initialize ONCE with your Figma/design dimensions
ResponsiveConfig.init(context, baseWidth: 390, baseHeight: 844);
return MaterialApp(
home: HomeScreen(),
);
}
}
Use anywhere without context! ✨ #
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: 200.w(), // ✅ No context needed!
height: 100.h(), // ✅ No context needed!
padding: 16.p, // ✅ No context needed!
margin: 20.m, // ✅ No context needed!
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.r()), // ✅ No context!
),
child: Text(
'Hello World',
style: TextStyle(
fontSize: 14.sp(), // ✅ No context needed!
),
),
),
);
}
}
Usage Examples #
🚀 Context-less Usage (Recommended) #
After initializing ResponsiveConfig.init(context) once, use extensions everywhere without context:
// Sizing
Container(
width: 300.w(), // No context! ✅
height: 200.h(), // No context! ✅
)
// Typography
Text(
'Title',
style: TextStyle(fontSize: 24.sp()), // No context! ✅
)
// Icons
Icon(Icons.home, size: 32.icon()) // No context! ✅
// Border Radius
BorderRadius.circular(16.r()) // No context! ✅
// Padding & Margin
Container(
padding: 16.p, // No context! ✅
margin: 20.m, // No context! ✅
child: Text('Hello'),
)
// Symmetric padding/margin
Container(
padding: 16.pSym(h: 24, v: 12), // No context! ✅
margin: 20.mSym(h: 16, v: 8), // No context! ✅
)
// Gaps
Column(
children: [
Text('Item 1'),
16.gapH, // No context! ✅
Text('Item 2'),
],
)
Row(
children: [
Text('Left'),
20.gapW, // No context! ✅
Text('Right'),
],
)
📱 With Context (Alternative) #
You can still use context-based extensions if needed:
Container(
width: 200.w(context),
height: 100.h(context),
padding: 16.p(context),
child: Text(
'Hello',
style: TextStyle(fontSize: 14.sp(context)),
),
)
// Full control with pOnly/pSymmetric
Container(
padding: 20.pSymmetric(
context: context,
horizontal: 24,
vertical: 12,
),
)
Container(
padding: 10.pOnly(
context: context,
left: 20,
top: 10,
right: 20,
bottom: 10,
),
)
// Gaps
Column(
children: [
Text('Item 1'),
16.gapH(context),
Text('Item 2'),
],
)
📱 Device Detection #
// Check device type (requires context)
if (context.isMobile) {
// Mobile layout
} else if (context.isTablet) {
// Tablet layout
} else if (context.isDesktop) {
// Desktop layout
}
// Get device type as string
print(context.deviceType); // "mobile", "tablet", or "desktop"
// Screen dimensions
print(context.screenWidth);
print(context.screenHeight);
// Orientation
if (context.isPortrait) {
// Portrait mode
} else if (context.isLandscape) {
// Landscape mode
}
🛡️ Safe Area #
// Get safe area insets (requires context)
Padding(
padding: EdgeInsets.only(
top: context.safeTop,
bottom: context.safeBottom,
left: context.safeLeft,
right: context.safeRight,
),
child: YourWidget(),
)
🎯 Responsive Values #
// Select values based on device type (requires context)
final columns = context.responsive<int>(
mobile: 2,
tablet: 3,
desktop: 4,
);
// Grid count helper
GridView.count(
crossAxisCount: context.gridCount(
mobile: 2,
tablet: 3,
desktop: 4,
),
)
// Responsive value based on breakpoints
final fontSize = context.responsiveValue<double>(
defaultValue: 14.0,
sm: 12.0, // < 600px
md: 14.0, // 600-1024px
lg: 16.0, // >= 1024px
);
🔧 Advanced Helpers #
// Auto text size with constraints (requires context)
TextStyle(
fontSize: context.autoTextSize(min: 12, max: 24),
)
// Responsive image size (requires context)
Size imgSize = context.imageSize(width: 300, height: 200);
// Min/Max scaling based on shortest/longest side
Container(
width: 100.min(context), // Based on shortest side
height: 100.max(context), // Based on longest side
)
// Responsive animation duration
Duration duration = context.animDuration(300); // 300ms base
// Card margin helper
Container(
margin: context.cardMargin(16),
)
Configuration #
Set Base Dimensions #
You can set your design dimensions in multiple ways:
// Option 1: During initialization (Recommended)
ResponsiveConfig.init(context, baseWidth: 375, baseHeight: 812);
// Option 2: Before initialization
ResponsiveConfig.setBaseDimensions(width: 375, height: 812);
ResponsiveConfig.init(context);
// Option 3: Platform-specific
if (Platform.isIOS) {
ResponsiveConfig.init(context, baseWidth: 390, baseHeight: 844);
} else {
ResponsiveConfig.init(context, baseWidth: 360, baseHeight: 800);
}
Default Base Dimensions #
If not specified, the default base dimensions are:
- Width: 390 (iPhone 14 Pro)
- Height: 844 (iPhone 14 Pro)
Check Configuration #
// Get current base dimensions
print(ResponsiveConfig.baseWidth); // 390.0
print(ResponsiveConfig.baseHeight); // 844.0
// Check if initialized
print(ResponsiveConfig.initialized); // true/false
// Get current screen dimensions
print(ResponsiveConfig.width); // Current screen width
print(ResponsiveConfig.height); // Current screen height
Responsive Builder Widget #
ResponsiveBuilder(
builder: (context, constraints) {
return Container(
width: constraints.maxWidth,
child: YourWidget(),
);
},
)
Breaking Points #
- Mobile: < 600px width
- Tablet: 600px - 1024px width
- Desktop: >= 1024px width
Complete Example #
import 'package:flutter/material.dart';
import 'package:adaptive_responsive_ui/adaptive_responsive_ui.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Initialize ONCE
ResponsiveConfig.init(context, baseWidth: 390, baseHeight: 844);
return MaterialApp(
title: 'Adaptive Responsive UI Demo',
home: HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Responsive Demo'),
),
body: SingleChildScrollView(
padding: 16.p, // No context needed! ✅
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Device Info
Text(
'Device: ${context.deviceType}',
style: TextStyle(fontSize: 18.sp()), // No context! ✅
),
16.gapH, // No context! ✅
// Responsive Container
Container(
width: 300.w(), // No context! ✅
height: 150.h(), // No context! ✅
padding: 20.p, // No context! ✅
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(12.r()), // No context! ✅
),
child: Center(
child: Text(
'Responsive Container',
style: TextStyle(
fontSize: 16.sp(), // No context! ✅
color: Colors.white,
),
),
),
),
20.gapH, // No context! ✅
// Symmetric Padding Example
Container(
padding: 16.pSym(h: 20, v: 10), // No context! ✅
color: Colors.green.shade100,
child: Text(
'Symmetric Padding',
style: TextStyle(fontSize: 14.sp()), // No context! ✅
),
),
20.gapH, // No context! ✅
// Device Type Display
Text('Screen: ${context.screenWidth.toInt()} x ${context.screenHeight.toInt()}'),
Text('Mobile: ${context.isMobile}'),
Text('Tablet: ${context.isTablet}'),
Text('Desktop: ${context.isDesktop}'),
],
),
),
);
}
}
For more examples, check out the example folder.
API Reference #
Context-less Extensions (After ResponsiveConfig.init()) #
| Extension | Description | Example |
|---|---|---|
.w() |
Responsive width | 200.w() |
.h() |
Responsive height | 100.h() |
.sp() |
Responsive font size | 14.sp() |
.icon() |
Responsive icon size | 24.icon() |
.r() |
Responsive radius | 12.r() |
.p |
Responsive padding (all sides) | 16.p |
.m |
Responsive margin (all sides) | 20.m |
.gapW |
Horizontal gap | 16.gapW |
.gapH |
Vertical gap | 16.gapH |
.pSym() |
Symmetric padding | 16.pSym(h: 20, v: 10) |
.mSym() |
Symmetric margin | 16.mSym(h: 20, v: 10) |
Context-based Extensions #
| Extension | Description | Example |
|---|---|---|
.w(context) |
Responsive width | 200.w(context) |
.h(context) |
Responsive height | 100.h(context) |
.sp(context) |
Responsive font size | 14.sp(context) |
.p(context) |
Padding (all sides) | 16.p(context) |
.pOnly() |
Padding (specific sides) | 16.pOnly(context: context, left: 20) |
.pSymmetric() |
Symmetric padding | 16.pSymmetric(context: context, horizontal: 20) |
.m(context) |
Margin (all sides) | 20.m(context) |
.mOnly() |
Margin (specific sides) | 20.mOnly(context: context, top: 10) |
.mSymmetric() |
Symmetric margin | 20.mSymmetric(context: context, vertical: 10) |
.gapW(context) |
Horizontal gap | 16.gapW(context) |
.gapH(context) |
Vertical gap | 16.gapH(context) |
.min(context) |
Scale by shortest side | 100.min(context) |
.max(context) |
Scale by longest side | 100.max(context) |
BuildContext Extensions #
| Property | Type | Description |
|---|---|---|
screenWidth |
double |
Current screen width |
screenHeight |
double |
Current screen height |
isMobile |
bool |
Is mobile device (< 600px) |
isTablet |
bool |
Is tablet device (600-1024px) |
isDesktop |
bool |
Is desktop device (>= 1024px) |
deviceType |
String |
"mobile", "tablet", or "desktop" |
isPortrait |
bool |
Is portrait orientation |
isLandscape |
bool |
Is landscape orientation |
safeTop |
double |
Top safe area padding |
safeBottom |
double |
Bottom safe area padding |
safeLeft |
double |
Left safe area padding |
safeRight |
double |
Right safe area padding |
pixelRatio |
double |
Device pixel ratio |
Tips & Best Practices #
- Initialize early: Call
ResponsiveConfig.init(context)in your main app widget's build method - Use design dimensions: Set
baseWidthandbaseHeightto match your Figma/design file exactly - Context-less by default: Use context-less extensions (
.w(),.sp(), etc.) for cleaner code - Use context when needed: Use context-based extensions when you need more control (like
.pOnly()) - Consistent scaling: Use
.w()for horizontal,.h()for vertical measurements - Font sizes: Always use
.sp()for text to maintain readability across devices - Test on devices: Test your responsive UI on different screen sizes (mobile, tablet, desktop)
- Hot restart after init: If you change base dimensions, do a hot restart (not hot reload)
Migration Guide #
From version 1.0.x to 1.1.0 #
Version 1.1.0 introduces context-less extensions. No breaking changes, but you can now simplify your code:
Before (1.0.x):
Container(
padding: 16.p(context),
margin: 20.m(context),
child: Text('Hello', style: TextStyle(fontSize: 14.sp(context))),
)
After (1.1.0):
Container(
padding: 16.p, // ✅ Simpler!
margin: 20.m, // ✅ Simpler!
child: Text('Hello', style: TextStyle(fontSize: 14.sp())), // ✅ Simpler!
)
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.
Author #
Mostafiz - GitHub
Support #
If you find this package helpful, please:
- ⭐ Star the repo on GitHub
- 👍 Like the package on pub.dev
- 🐛 Report issues on GitHub Issues
Changelog #
See CHANGELOG.md for a detailed list of changes.