Vize
Vize is a modern, developer-friendly Flutter package for effortless responsive UIs that match your Figma designs perfectly.
It offers two powerful approaches:
- Percentage-based scaling for flexible responsive layouts
- Figma-based scaling for pixel-perfect design implementation
Perfect for clean, production-ready apps and scalable design systems. No more responsiveness headaches!
Features
Features
- Dual Scaling Approaches:
- Percentage-Based Layouts: Flexible width/height sizing for responsive designs
- Figma-Based Scaling: Direct pixel-perfect scaling from your Figma designs
- Device Detection: Automatic mobile, tablet, desktop detection
- Orientation Support: Portrait and landscape handling
- Clean Syntax: Elegant extensions for both approaches
- Backward Compatible: All existing code continues to work
- Lightweight: Minimal overhead, maximum performance
- Flexible: Customizable breakpoints and scaling
Installation
Add this to your package's pubspec.yaml file:
dependencies:
vize: ^1.0.2
Then run:
flutter pub get
Quick Start
1a. Initialize Vize
Initialize Vize once in your app, typically in your root widget:
The best way to initialize Vize is inside the builder of your MaterialApp. This ensures Vize stays updated whenever the screen size changes.
import 'package:flutter/material.dart';
import 'package:vize/vize.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
builder: (context, child) {
// Initialize Vize
Vize.init(context, figmaWidth: 390, figmaHeight: 844);
return child!;
},
home: const HomePage(),
);
}
}
2. Choose Your Approach
Option A: Percentage-Based (Responsive Layouts)
Perfect for flexible designs that adapt to any screen:
Container(
width: 50.w, // 50% of screen width
height: 30.h, // 30% of screen height
padding: 16.pa, // Percentage-based padding
child: Text('Responsive Container'),
)
Option B: Figma-Based (Pixel-Perfect)
Perfect for implementing exact Figma designs:
Container(
width: 375.fw, // 375px scaled from Figma width
height: 40.fh, // 40px scaled from Figma height
padding: 16.pa, // Figma-based padding
child: Text('Figma Container'),
)
3. Use Reactive Layouts
Wrap your screens in VizeLayout for automatic responsiveness:
VizeLayout(
builder: (context, info) {
return Scaffold(
body: Container(
width: 100.w, // 100% of screen width (percentage-based)
padding: 20.pa, // Figma-based padding
child: Text("Device: ${info.device}"),
),
);
},
)
Usage Examples
When to Use Each Approach
| Use Case | Recommended Approach | Example |
|---|---|---|
| Full-screen containers | Percentage-based | width: 100.w, height: 50.h |
| Figma design elements | Figma-based | width: 375.fw, height: 40.fh |
| Responsive spacing | Percentage-based | 5.hs, 10.ws |
| Exact Figma spacing | Figma-based | 40.fhs, 100.fws |
| Adaptive layouts | Percentage-based | w(80), h(25) |
| Pixel-perfect UI | Figma-based | fw(200), fh(100) |
Percentage-Based Examples
Perfect for flexible, responsive designs:
Column(
children: [
Container(
width: w(100), // Full width (100%)
height: h(25), // 25% of screen height
color: Colors.blue,
),
hs(2), // 2% height spacing
Container(
width: w(80), // 80% width
height: h(50), // 50% height
color: Colors.green,
),
],
)
Figma-Based Examples
Implement exact designs from Figma:
// Using helper functions
Container(
width: fw(375), // 375px scaled from Figma
height: fh(40), // 40px scaled from Figma
padding: pa(16), // 16px scaled padding
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(r(12)), // Always uses Figma scaling
),
)
// Or with extensions (even cleaner!)
Container(
width: 375.fw,
height: 40.fh,
padding: 16.pa,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.r), // .r always uses Figma scaling
),
)
Mixed Approach
Combine both for the best results:
Container(
width: w(90), // 90% width (responsive)
height: 200.fh, // 200px from Figma (fixed ratio)
child: Column(
children: [
16.fhs, // Fixed 16px spacing from Figma
Text('Mixed Layout', style: TextStyle(fontSize: 16.ts)),
2.hs, // 2% responsive spacing
],
),
)
Device-Specific Layouts
Build different layouts for different devices:
VizeBuilder(
mobile: (context) => const MobileLayout(),
tablet: (context) => const TabletLayout(),
desktop: (context) => const DesktopLayout(),
)
Or use conditional logic:
@override
Widget build(BuildContext context) {
if (isMobile) {
return const MobileLayout();
} else if (isTablet) {
return const TabletLayout();
} else {
return const DesktopLayout();
}
}
Responsive Padding
Multiple ways to add responsive padding:
// Vize-based padding
Container(padding: pa(16)) // padding on all sides
Container(padding: 16.pa) // Same with extension
// Symmetric padding
Container(padding: ps(h: 20, v: 10)) // Percentage-based
Container(padding: Vize.I.ps(h: 20, v: 10)) // Figma-based
// Individual sides
Container(padding: po(l: 10, t: 20, r: 10, b: 20)) // Percentage-based
Spacing
Easy spacing between widgets:
// Percentage-based spacing
Column(
children: [
const Text('Item 1'),
hs(2), // 2% height spacing
2.hs, // Same with extension
const Text('Item 2'),
],
)
// Figma-based spacing
Column(
children: [
const Text('Item 1'),
fhs(40), // 40px spacing from Figma
40.fhs, // Same with extension
const Text('Item 2'),
],
)
Standard Spacing (8px Grid)
Use the sp() helper for standard spacing (always Figma-based):
Column(
children: [
const Text('Item 1'),
SizedBox(height: sp()), // 8px scaled from Figma
const Text('Item 2'),
SizedBox(height: sp(2)), // 16px scaled from Figma
const Text('Item 3'),
],
)
Adaptive Grid
Create responsive grid layouts:
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: adaptiveColumns(
mobile: 2,
tablet: 4,
desktop: 6,
),
crossAxisSpacing: sp(2), // 16px scaled spacing
mainAxisSpacing: sp(2),
),
itemBuilder: (context, index) => const Card(child: Text('Item')),
)
Adaptive Values
Return different values based on device:
final fontSize = adaptiveValue(
mobile: 14.0,
tablet: 16.0,
desktop: 18.0,
);
final columns = adaptiveValue(
mobile: 1,
tablet: 2,
desktop: 3,
);
Helper Functions
Percentage-Based Helpers
| Function | Description | Example |
|---|---|---|
w(percent) |
Width percentage | w(50) → 50% of screen width |
h(percent) |
Height percentage | h(25) → 25% of screen height |
ws(percent) |
Width spacing | ws(5) → SizedBox(width: 5%) |
hs(percent) |
Height spacing | hs(2) → SizedBox(height: 2%) |
ps({h, v}) |
Symmetric padding | ps(h: 20, v: 10) |
po({l, t, r, b}) |
Individual padding | po(l: 10, t: 20) |
Figma-Based Helpers
| Function | Description | Example |
|---|---|---|
fw(value) |
Figma-scaled width | fw(375) → 375px scaled |
fh(value) |
Figma-scaled height | fh(40) → 40px scaled |
fws(value) |
Figma width spacing | fws(100) → SizedBox(width: 100px scaled) |
fhs(value) |
Figma height spacing | fhs(40) → SizedBox(height: 40px scaled) |
sp([step]) |
Standard spacing | sp(2) → 16px scaled |
Shared Helpers
| Function | Description | Example |
|---|---|---|
ts(value) |
Scale text size | ts(16) → scaled 16px |
r(value) |
Scale radius | r(12) → scaled 12px |
isMobile |
Check if mobile | if (isMobile) ... |
isTablet |
Check if tablet | if (isTablet) ... |
isDesktop |
Check if desktop | if (isDesktop) ... |
adaptiveColumns() |
Responsive grid | adaptiveColumns(mobile: 2) |
Extensions
Percentage-Based Extensions
50.w // 50% of screen width
30.h // 30% of screen height
5.ws // SizedBox(width: 5%)
2.hs // SizedBox(height: 2%)
Figma-Based Extensions
375.fw // 375px scaled from Figma width
40.fh // 40px scaled from Figma height
100.fws // SizedBox(width: 100px scaled)
40.fhs // SizedBox(height: 40px scaled)
Shared Extensions
18.ts // Text size scaled
12.r // Radius scaled (always Figma-based)
Core Methods
| Method | Description | Example |
|---|---|---|
Vize.init(context) |
Initialize Vize | Vize.init(context) |
VizeLayout |
Reactive builder | VizeLayout(builder:...) |
Vize.getInfo() |
Get constraints | Vize.getInfo(context,..) |
Vize.I Methods
Access these via Vize.I:
| Method | Description |
|---|---|
Vize.I.wp(percent) |
Width percentage |
Vize.I.hp(percent) |
Height percentage |
Vize.I.sw(value) |
Scale width from Figma |
Vize.I.sh(value) |
Scale height from Figma |
Vize.I.ts(size) |
Scale text size |
Vize.I.r(value) |
Scale radius |
Vize.I.pa(value) |
Padding all sides |
Vize.I.ps({h, v}) |
Symmetric padding |
Vize.I.po({l, t, r, b}) |
Individual padding |
Vize.I.isMobile |
Check if mobile |
Vize.I.isTablet |
Check if tablet |
Vize.I.isDesktop |
Check if desktop |
Vize.I.device |
Get device type |
Device Flags
| Flag | Description |
|---|---|
isMobile |
True if mobile device |
isTablet |
True if tablet device |
isDesktop |
True if desktop device |
Widgets
| Widget | Description |
|---|---|
VizeBuilder |
Build different layouts per device |
VizeLayout |
LayoutBuilder with VizeInfo |
Best Practices
-
Initialize Once
Vize.init(context); // In MaterialApp builder -
Choose the Right Approach
- Use percentage-based (
w,h,hs,ws) for responsive layouts - Use Figma-based (
fw,fh,fhs,fws) for pixel-perfect designs
- Use percentage-based (
-
Mix Approaches Wisely
// Good: Responsive container with fixed elements Container( width: w(90), // Responsive width height: 200.fh, // Fixed height from Figma child: ... ) -
Test on Multiple Devices
- Small phones, tablets, and desktops
- Different orientations
Migration from 1.0.x
No migration needed! Version 1.0.2 is fully backward compatible:
- All existing percentage-based code continues to work
- New Figma-based functions are additive only
- No breaking changes to existing APIs
// Your existing code still works:
50.w // Still 50% of screen width
5.hs // Still 5% height spacing
// New features are available:
375.fw // New: 375px scaled from Figma
40.fhs // New: 40px height spacing from Figma
Complete Example
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
builder: (context, child) {
Vize.init(
context,
figmaWidth: 390,
figmaHeight: 844,
);
return child!;
},
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
padding: 20.pa, // padding
child: Column(
children: [
// Figma-based header
Container(
width: 375.fw, // 375px from Figma
height: 40.fh, // 40px from Figma
decoration: BoxDecoration(
color: Colors.blueAccent,
borderRadius: BorderRadius.circular(12.r),
),
child: Center(
child: Text(
'Header',
style: TextStyle(fontSize: 22.ts),
),
),
),
40.fhs, // Fixed spacing from Figma
// Responsive grid
GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: 4,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: adaptiveColumns(
mobile: 1,
tablet: 2,
desktop: 4,
),
mainAxisSpacing: sp(2),
crossAxisSpacing: sp(2),
),
itemBuilder: (context, i) => Container(
color: Colors.grey[200],
alignment: Alignment.center,
child: Text('Item $i'),
),
),
2.hs, // Responsive spacing at bottom
],
),
),
);
}
}
Contributing
Contributions are very welcome! We'd love to see new features, bug fixes, documentation improvements, or anything that makes Vize better.
Local Development & Verification
To ensure consistency, we've included a "pre-flight" script. Before pushing your changes or opening a PR, please run the checks to verify formatting, analysis, and tests:
On Windows (PowerShell):
./check.ps1
On Linux/macOS:
chmod +x check.sh # Only needed the first time
./check.sh
How to Contribute
- Fork the repository
- Create a feature branch
git checkout -b new-feature - Commit your changes
git commit -am 'Add new feature' - Push to the branch
git push origin new-feature - Open a Pull Request on GitHub
Please make sure your code follows the existing style, includes tests where appropriate, and updates documentation if needed.
Thank you for helping improve Vize!
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
If you find Vize helpful, please give it a ⭐ on GitHub!
Contact
- Issues: GitHub Issues
Made with ❤️ for the Flutter community