Is there a way to get near pixel perfect scaling of widgets in Flutter?

Viewed 54

For my use-case I need layout to be consistently same at different screen widths.

For this I'm using a simple scale by reference and multiplying each widget's height by this scale value. However, the fidelity of the placement is not very accurate, or varies wildly on different screen sizes. I suspect it is to do with the limitation of Flutters logical pixels which are said to be about 38 per centimeter. Especially on small screens (pretty much any mobile) that is not enough to get even close to pixel perfection, and especially text alignments are suffering.

gif showing the problem

Test code: run on desktop and change the width of the app to see how the margin between the texts jump up and down as if it's snapping the widgets to closest logical pixel. And the same goes for the text's own bounding box, making the issue worse in case the texts snap in opposite directions:

import 'package:flutter/material.dart';
void main() {
  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {

  double getScale(BuildContext context) {
    const double referenceWidth = 400;
    double screenWidth = MediaQuery.of(context).size.width;
    double fraction = screenWidth / referenceWidth;
    return fraction;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Text1',
                style: TextStyle(
                  fontSize: 14 * getScale(context),
                  backgroundColor: Colors.green,
                )
            ),
            Text(
              "text2",
              style: TextStyle(
                fontSize: 14 * getScale(context),
                backgroundColor: Colors.red,
              )
            ),
          ],
        ),
      ),
    );
  }
}

I've tried wrapping in a Transform.scale but it gives the same result in addition to lower quality visuals.

Am I missing something obvious?

EDIT: It seems like the responsive_framework library partially fixes the proiblem: Margin between the widgets to be scaled properly. But the Texts still vary their position within their Text widgets. I can't figure out why this works though.

1 Answers

not sure its exactly pixel perfect but I've managed to get it looking smoother using the Sizer package

See gif example

import 'package:flutter/material.dart';
import 'package:sizer/sizer.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return Sizer(builder: (context, orientation, deviceType) {
      return const MaterialApp(
        home: MyHomePage(),
      );
    });
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: LayoutBuilder(builder: (context, constraints) {
        return Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text('Text1',
                  style: TextStyle(
                    fontSize: 14.w,
                    backgroundColor: Colors.green,
                  )),
              Text("text2",
                  style: TextStyle(
                    fontSize: 14.w,
                    backgroundColor: Colors.red,
                  )),
            ],
          ),
        );
      }),
    );
  }
}
Related