UiH - UI Helper for Flutter
A complete responsive UI toolkit for Flutter apps across all platforms
Build beautiful, adaptive interfaces with clean syntax and powerful features.
Perfect pixel scaling, adaptive layouts, and rich Flutter integration—all in one package.
Features at a Glance
- Dual API Design - Context-free AND context-based APIs for maximum flexibility
- Material Design 3 - Built-in MD3 breakpoints for mobile, tablet, and desktop
- Modern Accessibility - Uses Flutter's latest
textScalerAPI with full accessibility support - Rich Integration - 50+ BuildContext extensions for Theme, Navigation, and more
- Advanced Font Scaling - Diagonal-based and breakpoint-based strategies
- Percentage Layouts - Screen fraction support for fluid responsive designs
- Multiple Naming Options - Choose between terse, descriptive, or full names
- Complete Toolkit - Responsive sizing + Theme helpers + Navigation + Utilities
- Cross-Platform - Mobile, Web, Desktop, and Fuchsia support
- Future-Proof - Uses modern Flutter APIs, no deprecated code
Quick Example
import 'package:flutter/material.dart';
import 'package:uih/uih.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
// Wrap your app once with UihInit
return UihInit(
designSize: const Size(414, 896), // Your design dimensions
builder: (context) => MaterialApp(
home: const HomePage(),
),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.all(20.w), // Responsive padding
child: Column(
children: [
// Responsive dimensions
Container(
width: 300.w, // Scales to screen width
height: 200.h, // Scales to screen height
decoration: BoxDecoration(
color: context.colorScheme.primary,
borderRadius: BorderRadius.circular(16.w),
),
child: Center(
child: Text(
'Hello UiH!',
style: TextStyle(
fontSize: 24.sp, // Responsive font
color: Colors.white,
),
),
),
),
// Responsive spacing
context.verticalSpacer(size: 24),
// Adaptive font based on device type
Text(
'Adaptive Typography',
style: TextStyle(fontSize: 18.af), // Larger on tablets
),
// Percentage-based layout
Container(
width: 0.8.wf, // 80% of screen width
height: 0.3.hf, // 30% of screen height
color: context.colorScheme.secondary,
),
// Adaptive layout
if (context.isTablet || context.isDesktop)
const Text('Tablet/Desktop Layout')
else
const Text('Mobile Layout'),
],
),
),
);
}
}
Getting Started
Installation
Add UiH to your pubspec.yaml:
dependencies:
uih: ^1.0.6
Then run:
flutter pub get
Basic Setup
Wrap your app with UihInit:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return UihInit(
designSize: const Size(414, 896), // Optional, defaults to iPhone 11 Pro Max
builder: (context) => MaterialApp(
home: HomePage(),
),
);
}
}
Alternative: Use with MaterialApp.builder:
MaterialApp(
builder: Uih.builder(
designSize: const Size(414, 896),
),
home: HomePage(),
)
That's it! Now use responsive extensions anywhere in your app.
Complete Feature Guide
Responsive Sizing
Scale your UI elements proportionally across all screen sizes.
// Width scaling
Container(width: 100.w) // Scales based on screen width
// Height scaling
Container(height: 50.h) // Scales based on screen height
// Context-based alternative (no initialization needed)
Container(
width: 100.rw(context),
height: 50.rh(context),
)
Font Scaling
Two powerful font scaling strategies:
1. Smooth Diagonal-Based Scaling
Text(
'Body Text',
style: TextStyle(fontSize: 16.sp), // Scales smoothly based on screen diagonal
)
// With accessibility control
Text(
'Icon Label',
style: TextStyle(
fontSize: 12.sp, // Respects user's text scale by default
),
)
2. Adaptive Breakpoint-Based Scaling
Text(
'Heading',
style: TextStyle(fontSize: 24.af), // Scales in steps: 0.8x → 1.0x → 1.1x → 1.2x
)
// Perfect for headings on different devices:
// - Small phones (<360dp): 0.8x smaller
// - Normal phones (360-479dp): 1.0x normal
// - Large phones/tablets (480-767dp): 1.1x larger
// - Tablets/desktop (≥768dp): 1.2x much larger
Percentage-Based Layouts
Create fluid layouts with screen fractions:
// 90% of screen width
Container(width: 0.9.wf)
// 30% of screen height
Container(height: 0.3.hf)
// Perfect for responsive grids
Row(
children: [
Container(width: 0.3.wf, child: Sidebar()),
Container(width: 0.7.wf, child: Content()),
],
)
Material Design 3 Breakpoints
Built-in device type detection using official Material Design 3 guidelines:
// Detect device type
if (context.isMobile) {
// < 600dp - Phones
return MobileLayout();
} else if (context.isTablet) {
// 600-840dp - Tablets
return TabletLayout();
} else {
// ≥ 840dp - Desktop
return DesktopLayout();
}
// Also available context-free
if (Uih.isMobile) { ... }
if (Uih.isTablet) { ... }
if (Uih.isDesktop) { ... }
Theme Integration
Access Flutter theme data with clean, concise syntax:
// Color scheme
final primary = context.colorScheme.primary;
final surface = context.colorScheme.surface;
final error = context.colorScheme.error;
// Text theme
final headline = context.textTheme.headlineMedium;
final body = context.textTheme.bodyLarge;
// Theme properties
final isDark = context.brightness == Brightness.dark;
final typography = context.typography;
Navigation Helpers
Simplified navigation and UI component access:
// Navigator
context.navigator.push(MaterialPageRoute(...));
// Focus
context.focusScope.unfocus();
// SnackBar (one-liner!)
context.showSnackBar(
SnackBar(content: Text('Success!')),
);
// Scaffold
final scaffold = context.scaffold;
Responsive Spacers
Semantic spacing widgets that scale with screen size:
// Horizontal spacing
Row(
children: [
Text('Left'),
context.horizontalSpacer(size: 16, multiplier: 2), // 32dp spacing
Text('Right'),
],
)
// Vertical spacing
Column(
children: [
Text('Top'),
context.verticalSpacer(size: 24), // 24dp spacing
Text('Bottom'),
],
)
Screen Dimensions
Access screen measurements in various units:
// Pixels
final width = context.widthInPixels;
final height = context.heightInPixels;
final diagonal = context.diagonalInPixels;
// Inches (real-world measurements)
final widthInch = context.widthInInches;
final heightInch = context.heightInInches;
final diagonalInch = context.diagonalInInches;
// Points (platform-independent)
final size = context.sizeInPoints;
final diagonal = context.diagonalInPoints;
// Pixels per inch (platform-aware)
final ppi = context.pixelsPerInch; // 150 for mobile, 96 for desktop
Orientation Detection
// Check orientation
if (context.isLandscape) {
return LandscapeLayout();
} else {
return PortraitLayout();
}
Multiple Naming Options
Choose the naming style that fits your team's standards:
// Terse (fast coding)
Container(width: 100.w, height: 50.h)
// Descriptive (context-based)
Container(
width: 100.rw(context),
height: 50.rh(context),
)
// Fully descriptive (self-documenting)
Container(
width: 100.relativeWidth(context),
height: 50.relativeHeight(context),
)
Bonus Utilities
WidgetState Helpers
MaterialStateProperty.resolveWith((states) {
if (states.isPressed) return Colors.red;
if (states.isHovered) return Colors.blue;
if (states.isFocused) return Colors.green;
return Colors.grey;
});
String Extensions
'user@example.com'.isEmail() // true
'hello world'.toCamelCase() // 'helloWorld'
'hello'.capitalize() // 'Hello'
'Hello'.reverse() // 'olleH'
Iterable Extensions
states.containsAny([WidgetState.hovered, WidgetState.pressed])
Dual API Design
UiH uniquely offers both context-free and context-based APIs. Choose what works best for your use case:
Context-Free API (Recommended)
Perfect for most scenarios. Clean, concise syntax.
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 100.w, // Clean and simple
height: 50.h,
child: Text('Hello', style: TextStyle(fontSize: 14.sp)),
);
}
}
Requires: One-time initialization with UihInit wrapper.
Context-Based API
No initialization needed. Perfect for testing or when context is readily available.
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 100.rw(context), // No initialization needed
height: 50.rh(context),
child: Text('Hello', style: TextStyle(fontSize: 14.sf(context))),
);
}
}
Requires: Nothing! Works immediately.
Mix and match based on your needs. Both APIs coexist perfectly.
API Reference
Numeric Extensions
All methods work with both int and double.
| Extension | Context-Free | Context-Based | Description |
|---|---|---|---|
| Width | 100.w |
100.rw(context) |
Relative width scaled to design |
| Height | 50.h |
50.rh(context) |
Relative height scaled to design |
| Font (Scaled) | 14.sp |
14.sf(context) |
Diagonal-based font scaling |
| Font (Adaptive) | 24.af |
24.afc(context) |
Breakpoint-based font scaling |
| Width Fraction | 0.5.wf |
0.5.wfc(context) |
50% of screen width |
| Height Fraction | 0.3.hf |
0.3.hfc(context) |
30% of screen height |
BuildContext Extensions
| Extension | Type | Description |
|---|---|---|
| Screen Dimensions | ||
context.mediaQuery |
MediaQueryData |
MediaQuery instance |
context.sizeInPixels |
Size |
Screen size in pixels |
context.widthInPixels |
double |
Screen width in pixels |
context.heightInPixels |
double |
Screen height in pixels |
context.diagonalInPixels |
double |
Screen diagonal in pixels |
context.sizeInPoints |
Size |
Screen size in points |
context.sizeInInches |
Size |
Screen size in inches |
context.widthInInches |
double |
Screen width in inches |
context.heightInInches |
double |
Screen height in inches |
context.diagonalInInches |
double |
Screen diagonal in inches |
context.pixelsPerInch |
double |
Platform-aware PPI |
| Responsive Sizing | ||
context.relativeWidth(value) |
double |
Scale width to screen |
context.relativeHeight(value) |
double |
Scale height to screen |
context.widthFraction(fraction) |
double |
Percentage of width (0.0-1.0) |
context.heightFraction(fraction) |
double |
Percentage of height (0.0-1.0) |
| Font Scaling | ||
context.scaledFontSize(size) |
double |
Diagonal-based scaling |
context.adaptiveFontSize(size) |
double |
Breakpoint-based scaling |
| Platform Detection | ||
context.isMobile |
bool |
< 600dp (Material Design 3) |
context.isTablet |
bool |
600-840dp (Material Design 3) |
context.isDesktop |
bool |
≥ 840dp (Material Design 3) |
context.isLandscape |
bool |
Landscape orientation |
| Theme Shortcuts | ||
context.theme |
ThemeData |
Theme instance |
context.textTheme |
TextTheme |
Text theme |
context.colorScheme |
ColorScheme |
Color scheme |
context.backgroundColor |
Color |
Background/surface color |
context.typography |
Typography |
Typography |
context.brightness |
Brightness |
Light/dark mode |
| Navigation | ||
context.navigator |
NavigatorState |
Navigator instance |
context.focusScope |
FocusScopeNode |
Focus scope |
context.scaffold |
ScaffoldState |
Scaffold instance |
context.showSnackBar(snackBar) |
void |
Show snack bar |
| Spacing Widgets | ||
context.horizontalSpacer(...) |
SizedBox |
Horizontal spacing |
context.verticalSpacer(...) |
SizedBox |
Vertical spacing |
Common Design Sizes
Use these preset sizes when initializing UiH:
// iPhone 11 Pro Max (default)
UihInit(designSize: const Size(414, 896))
// iPhone X / 11 Pro
UihInit(designSize: const Size(375, 812))
// iPhone 13 / 14
UihInit(designSize: const Size(390, 844))
// Google Pixel 5
UihInit(designSize: const Size(393, 851))
// iPad Pro 11"
UihInit(designSize: const Size(834, 1194))
// Common Android
UihInit(designSize: const Size(360, 640))
Best Practices
1. Initialize Once at App Root
// GOOD
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return UihInit(
builder: (context) => MaterialApp(...),
);
}
}
// AVOID - Don't initialize on every screen
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
Uih.init(context); // Not needed!
return Scaffold(...);
}
}
2. Use Semantic Spacers
// BETTER - Semantic and clear
Column(
children: [
Widget1(),
context.verticalSpacer(size: 16),
Widget2(),
],
)
// NOTE: Works but less clear
Column(
children: [
Widget1(),
SizedBox(height: 16.h),
Widget2(),
],
)
3. Choose the Right Font Scaling
// Body text - Use smooth scaling
Text('Body text', style: TextStyle(fontSize: 14.sp))
// Headings - Use adaptive scaling for more prominence on tablets
Text('Heading', style: TextStyle(fontSize: 24.af))
4. Respect Accessibility
Both font scaling methods respect user accessibility settings by default:
// Respects user's text scale
Text('Hello', style: TextStyle(fontSize: 16.sp))
// Only disable if absolutely necessary (e.g., icons)
Text('Search', style: TextStyle(fontSize: 24.sp)) // Still respects by default
5. Use Material Design 3 Breakpoints
// Use built-in breakpoints
if (context.isMobile) { ... }
if (context.isTablet) { ... }
if (context.isDesktop) { ... }
// AVOID magic numbers
if (context.widthInPixels < 600) { ... } // Harder to maintain
Troubleshooting
"UiH has not been initialized!"
Solution: Wrap your app with UihInit:
return UihInit(
builder: (context) => MaterialApp(...),
);
Or use the context-based API (no initialization needed):
Container(width: 100.rw(context))
Screen doesn't adapt to rotation
Solution: UiH automatically handles rotation with UihInit. If using manual initialization, reinitialize on rotation:
// Not needed with UihInit - it handles this automatically
Values seem incorrect
Solution: Check your design size matches your design tool:
UihInit(
designSize: const Size(375, 812), // Match your Figma/Sketch design
builder: (context) => MaterialApp(...),
)
Contributing
We welcome contributions! Here's how you can help:
- Fork the repository
- Create your feature branch:
git checkout -b my-new-feature - Commit your changes:
git commit -am 'Add amazing feature' - Push to the branch:
git push origin my-new-feature - Submit a pull request
Development Setup
# Clone the repo
git clone https://github.com/adaptolearning/UiH.git
cd UiH
# Install dependencies
flutter pub get
# Run tests
flutter test
# Run example app
cd example
flutter run
Reporting Issues
Found a bug or have a feature request? Open an issue on GitHub.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Show Your Support
If UiH helps you build better Flutter apps, give it a star on GitHub!
Additional Resources
Made with ❤️ by the princeteck