omni_pixel 0.3.1
omni_pixel: ^0.3.1 copied to clipboard
Figma-first responsive sizing for Flutter. Register the device frames your designers use, then write 48.opx to get pixel-perfect scaling on any screen.
import 'package:flutter/material.dart';
import 'package:omni_pixel/omni_pixel.dart';
void main() => runApp(const OmniPixelExampleApp());
class OmniPixelExampleApp extends StatelessWidget {
const OmniPixelExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'omni_pixel example',
theme: ThemeData(colorSchemeSeed: Colors.indigo, useMaterial3: true),
// Put OmniBuilder in MaterialApp.builder so every route is covered.
builder: (context, child) => OmniBuilder(
// The Figma frames your designers used:
devices: const [
OmniDevices.iphone17,
],
// Fine-tuning per aspect-ratio range and platform:
multipliers: const [
// Very tall Androids render a touch smaller.
OmniMultiplier.above(2.2, 0.97, platform: OmniPlatform.android),
// Squarish screens (tablets, split screen) get a little more air.
OmniMultiplier.below(1.6, 1.05),
],
// Floating bubble with everything OmniPixel resolved. Drag it
// around, tap to expand. Hidden in release builds.
debugOverlay: true,
child: child!,
),
home: const DemoPage(),
);
}
}
/// Recreates a card specced in Figma on the registered iPhone 17 frame:
/// card 343 wide, radius 16, padding 20, title 20, body 14, button 48.
class DemoPage extends StatelessWidget {
const DemoPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('omni_pixel')),
body: Center(
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// --- The Figma card, written with the exact Figma numbers ---
Container(
width: 343.opx,
padding: EdgeInsets.all(20.opx),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(16.opx),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Pixel-perfect from Figma',
style: TextStyle(
fontSize: 20.opx,
fontWeight: FontWeight.w700,
),
),
SizedBox(height: 8.opx),
Text(
'Every number on this card is the raw value from the '
'design file, written as value.opx. It scales with the '
'longest side of this screen relative to the design '
'frame.',
style: TextStyle(fontSize: 14.opx, height: 1.4),
),
SizedBox(height: 20.opx),
SizedBox(
width: double.infinity,
height: 48.opx, // 48 px button in Figma
child: FilledButton(
onPressed: () {},
child: Text(
'Continue',
style: TextStyle(fontSize: 16.opx),
),
),
),
],
),
),
SizedBox(height: 4.dh), // classic helpers still work
// --- What the helpers evaluate to on this screen ---
// Everything OmniPixel *resolved* (design device, scale,
// multiplier, device class...) lives in the floating debug
// bubble instead — see debugOverlay above.
Container(
width: 90.dw,
padding: EdgeInsets.all(12.opx),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondaryContainer,
borderRadius: BorderRadius.circular(12.opx),
),
child: DefaultTextStyle(
style: TextStyle(
fontSize: 12.opx,
fontFamily: 'monospace',
color: Theme.of(context).colorScheme.onSurface,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('48.opx : ${48.opx.toStringAsFixed(2)}'),
Text('10.h : ${10.h.toStringAsFixed(2)}'),
Text('10.w : ${10.w.toStringAsFixed(2)}'),
],
),
),
),
SizedBox(height: 16.opx),
// --- Device class + orientation, live ---
OmniResponsiveBuilder(
watch: _chip('watch', Colors.purple),
mobile: _chip('mobile', Colors.blue),
tablet: _chip('tablet', Colors.orange),
desktop: _chip('desktop', Colors.red),
),
SizedBox(height: 8.opx),
OmniOrientationBuilder(
portrait: (context) => _chip('portrait', Colors.green),
landscape: (context) => _chip('landscape', Colors.pink),
),
],
),
),
),
);
}
/// A colored pill used by the responsive / orientation demos above.
Widget _chip(String label, Color color) => Container(
padding: EdgeInsets.symmetric(horizontal: 16.opx, vertical: 8.opx),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(999),
),
child: Text(
label,
style: TextStyle(color: Colors.white, fontSize: 14.opx),
),
);
}