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 consistentMediaQuery size, safe-area padding, view insets (keyboard), and display features are rewritten into design-space units, so SafeArea, 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.min and/or a maxScale (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 textScaler in your MaterialApp.builder as you normally would.
  • MoAdapt reads 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.