skip to Main Content

I need a grid of identical widgets in my app. The widgets are square and there must be no gap between them. I can’t get this working, as there are always seemingly random gaps between some of the widgets. Sometimes there are a lot of gaps, sometimes just between a few of them, depending on the size of the app window.

Here’s the smallest example I could come up with that displays the problem:

import 'package:flutter/material.dart';

void main() => runApp(const TestApp());

class TestApp extends StatelessWidget {
  const TestApp();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      routes: {
        '/': (context) => const Test(),
      },
    );
  }
}

class Test extends StatelessWidget {
  const Test();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey[200],
      body: GridView.count(
        crossAxisCount: 7,
        children: List.generate(49, (index) {
        return Container(
          width: double.infinity,
          height: double.infinity,
          decoration: const BoxDecoration(
            color: Colors.black,
          ));
        }),
      ),
    );
  }
}

If I run this in one of the embedded DartPads on flutter.dev, this is what I get:
Gaps between all widgets

Moving the separator between code and rendering window, I might get this instead:
Gaps between some of the widgets

Because the GridView defaults to 0.0 padding between widgets, I would have expected a huge, perfectly black square to be the result. Where do the gray borders between (some of) the widgets come from, and how do I get rid of them?

3

Answers


  1. This is a known problem. See:

    As a workaround I came up with, you can set a Border with a width of 0 to the BoxDecoration:

     Container(
                width: double.infinity,
                height: double.infinity,
                decoration: BoxDecoration(
                  color: Colors.black,
                  border: Border.all(width: 0), // <-- Set this
                ),
              );
    

    import 'package:flutter/material.dart';
    
    void main() => runApp(const TestApp());
    
    class TestApp extends StatelessWidget {
      const TestApp();
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          routes: {
            '/': (context) => const Test(),
          },
        );
      }
    }
    
    class Test extends StatelessWidget {
      const Test();
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: Colors.grey[200],
          body: GridView.count(
            crossAxisCount: 7,
            children: List.generate(49, (index) {
              return Container(
                width: double.infinity,
                height: double.infinity,
                decoration: BoxDecoration(
                    color: Colors.black, border: Border.all(width: 0)),
                child: Text(
                  'hi',
                  style: TextStyle(color: Colors.white),
                ),
              );
            }),
          ),
        );
      }
    }
    
    Login or Signup to reply.
  2. This is happening when, in this case, the width given to the GridView is not a multiple of the number of columns. There are a few pixels left over, and these are the gaps you see.

    If you restrict to a multiple of the number of columns then the gaps disappear. e.g.

    class Test extends StatelessWidget {
      const Test({super.key});
    
      final numColumns = 7;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: Colors.grey[200],
          body: SizedBox(
            width: _width(context),
            child: GridView.count(
              crossAxisCount: numColumns,
              children: List.generate(numColumns * numColumns, (index) {
                return Container(
                  width: double.infinity,
                  height: double.infinity,
                  decoration: const BoxDecoration(color: Colors.black),
                );
              }),
            ),
          ),
        );
      }
    
      double _width(BuildContext context) {
        final w = MediaQuery.sizeOf(context).width;
    
        return (w ~/ numColumns) * numColumns.toDouble();
      }
    
    Login or Signup to reply.
  3. Why is this happening?

    "You’re drawing a series of rectangles adjacent to each other, on top of a different background color, but they don’t quite line up with the physical pixel boundaries. This means we try to antialias the edges, and each one is painted on top of the background, so the background shows up in the antialiasing."

    What’s the solution?

    1. "The second is to wrap all those widgets with a RepaintBoundary, which will cause them to all be painted as one graphic which is then anti-aliased once with the parent. That might not solve the issue, depending on exactly what the alignment ends up being, but it should be better."
    2. "In general though the right answer is the first suggestion here; don’t put two adjacent boxes with the same background color, just put the background color on the common background."

    Source: https://github.com/flutter/flutter/issues/15035#issuecomment-370028740

    Using suggestion 2., the reproducible example you provided is fixed:

    Before:

    enter image description here

    After:

    enter image description here

    Changes (see comments):

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        body: ColoredBox(
          /// provide a background color which is the same color as the GridView's
          /// cells background color.
          color: Colors.black,
          child: GridView.count(
            ...
          ),
        ),
      );
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search