mo_adapt
Wrap your app once and every dimension in your UI — text, padding, margins,
SizedBoxes, icon sizes, border radii, widget widths and heights — scales
proportionally to the screen, based on a reference design size.
No .w / .h / .sp accessors. No changes to any child widget. No custom
bindings or main.dart surgery.
Quick start
import 'package:flutter/material.dart';
import 'package:mo_adapt/mo_adapt.dart';
void main() {
runApp(
const MoAdapt(
designSize: Size(390, 844), // the frame size your design was made for
child: MyApp(), // your existing MaterialApp, unchanged
),
);
}
That's it. Build your UI with the exact pixel values from your design
(fontSize: 16, EdgeInsets.all(12), SizedBox(height: 24), …) and they
render proportionally on every screen.
How it works
MoAdapt lays your app out in your design's coordinate space and paints it
through a single uniform transform that fills the real screen:
- Everything scales together — any fixed logical dimension anywhere in the subtree, including in packages you don't control.
- Pixel-crisp — the transform is a vector (paint-time) operation, not a bitmap stretch, so text and shapes stay sharp at any scale. The device pixel ratio reported to the framework is raised accordingly, so images pick appropriately high-resolution variants.
- The framework stays consistent —
MediaQuerysize, safe-area padding, view insets (keyboard), and display features are rewritten into design-space units, soSafeArea, dialogs, bottom sheets, and keyboard avoidance all keep working. - Touch just works — pointer events are mapped through the transform, so taps land where things visually are.
Choosing how to scale
| Parameter | Default | Meaning |
|---|---|---|
designSize |
required | Logical size of your reference design frame. |
scaleMode |
MoAdaptScaleMode.width |
width, height, min (fit), or max (cover). |
minScale / maxScale |
none | Clamp the computed scale factor. |
enabled |
true |
Set false to render 1:1 (state is preserved when toggling). |
Recommendations:
- Portrait-only phone app — the default (
width) is what you want: the layout spans the full width exactly as designed and content scrolls vertically as usual. - Landscape / tablets / desktop — use
scaleMode: MoAdaptScaleMode.minand/or amaxScale(e.g.1.3) so the UI doesn't blow up on wide screens.
Reading the current scale
final adapt = MoAdapt.of(context);
adapt.scale; // e.g. 1.42
adapt.adaptedSize; // what MediaQuery.sizeOf reports inside MoAdapt
To keep one specific widget at its real size (opt out of scaling), divide by the scale:
SizedBox(width: MoAdapt.of(context).unscale(48)) // exactly 48 real logical px
Notes
- The system accessibility text scale still applies on top of the
proportional scaling — users who enlarge their fonts keep that ability. If
you don't want that, clamp
textScalerin yourMaterialApp.builderas you normally would. MoAdaptreads its metrics straight from the window, so it responds to rotation, window resizing, and keyboard changes automatically.
Libraries
- mo_adapt
- Proportional UI scaling for an entire Flutter app from a single wrapper widget.