Make your Flutter Apps pixel-perfect right away

Aldy Chrissandy
CodeX
Published in
5 min readAug 2, 2021

Building standard UI in Flutter is easy and fast. Using a lot of widget collections from the Flutter Team and community, you can build a simple UI in both platforms with user-friendly and intuitive language.

In Flutter, you can nest widgets as deep as you want without worrying about the performance, and nesting the Widget is recommended in Flutter. In fact, if you have a huge widget, then big chance you can or need to split it into smaller widgets.

Building a good app is hard, but building a pixel-perfect one is just 100-times harder, especially if you have a big team without proper code convention. At some point, you will have a big collection of Widgets that comes with many defaults optional values, and configurations.

If you have a client who is not worrying about all of the UI detail, you do need to read further. If your client or boss has attention to detail, you need some extra time to check all the UI down to pixels. It goes without saying how confusing it is when your client says the size is wrong but you can’t really tell the difference.

Let me show you the possibility of what usually mess up, resulting in a task to fix the 1-pixel problem in Flutter.

The easiest example is let’s say that you need to create one card with a margin or padding 12px on left and right, and you ended up writing this code below.

Maybe you will assume because you have an 8px margin from Container, you only need to add 4px padding to make Card Widget on line 57 have 12px, but on screen, it’s not 12px, it’s 16px. There are four extra pixels from the default margin from Container in Card Widget.

This is just one example of tons of Widgets that you can use from Flutter, and custom Widgets created by your team. Ideally, the Widget can’t have Padding or Margin. The Widgets you created shouldn’t have any size as well, all sizes should be controlled by the parent’s Widgets, but mistakes or exceptions always happen in projects. Thus, let's discuss how to make your life easier.

What’s Next

To make sure your UI by pixel, you can use this awesome Flutter Pixel Perfect Package to put overlay pictures on top of your apps. It’s similar to PerfectPixel in chrome.

The problem is it’s troublesome to put every picture from the design team and put it in the assets folder every time you want to check the pixel-perfect on your UI, so the most practical way to check the size is by a ruler. If you’re a Mac user, maybe you know about the xScope app or Redline tools that has a feature for drawing overlay grid by pixel on the whole screen.

Drawing a grid on top of your existing apps in Flutter is straightforward. If you use MaterialApp as your Main Widget, then you only need to draw the ruler in the builder function.

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
builder: (context, widget) {
return Text('Im always here on top of all your screen');
},
home: MyHomePage(),
);
}
}

The next problem is how to draw a square of 8px to fill the whole screen on top of your apps?

The simple way is using SizedBox(height: 8, width: 8,) and fill the whole screen with SizedBo. However, if you do, you will draw a lot of SizedBox Widgets on top of your apps as well. Alternatively, you can draw it manually using CustomPainter. You only need to know the device width and height, then using drawRect to draw an 8px square box for the whole screen.

class GridPainter extends CustomPainter {
double deviceWidth;
double deviceHeight;

GridPainter(this.deviceWidth, this.deviceHeight);

@override
void paint(Canvas canvas, Size size) {
var paint = Paint()
..strokeWidth = 1
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;

for (var i = -(deviceHeight / 2); i < deviceHeight; i += 8) {
for (var j = -(deviceWidth / 2); j < deviceWidth; j += 8) {
canvas.drawRect(
Offset(j.toDouble(), i.toDouble()) & Size(8, 8),
paint);
}
}
}

@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}

Next, use your GridPainter in your Widget that you need to return in the builder function on MaterialApp Widget.

@override
Widget build(BuildContext context) {
var width = MediaQuery.of(context).size.width;
var height = MediaQuery.of(context).size.height;
return Stack(
children: [
widget.child,
Center(
child: IgnorePointer(
child: CustomPaint(
painter: GridPainter(width, height),
),
),
),
],
);
}

After that, you just need to think of your own implementation on how to hide or show the grid, by right you can comment on the code and enable it back when you need to show the ruler. Now, you can quickly verify the size of your widgets in your app.

Try it by yourself below by double click black icon on top of the Demo Page title.

Now What?

If you need a shortcut, you can check this package.

Now, if your boss or client complain and tell you there is one pixel out of alignment, you can show the ruler and discuss more critical issue other than that one pixel out of alignment, but don’t forget if your client or your tester know about this, they also can spot the miss-alignment easily.

--

--