Search code examples
flutterflutter-layout

Flutter: Clip a Column or Row to prevent overflow


I have a layout structure in Flutter like this:

Inkwell
  Card
    ScopedModelDescendant
      Column
        Container[]

The number of containers in the column is variable.

The goal is that it should look like this:

expected look

But instead, it ends up doing this:

actual look

I've tried adding a clipBehavior property to the Card, and I've tried mixing in ClipRects anywhere in the structure, but nothing seems to work. My best guess is that a ClipRect above the Column doesn't help because the overflow happens within the column.

This is the error I'm getting:

flutter: ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
flutter: The following message was thrown during layout:
flutter: A RenderFlex overflowed by 15 pixels on the bottom.
flutter:
flutter: The overflowing RenderFlex has an orientation of Axis.vertical.
flutter: The edge of the RenderFlex that is overflowing has been marked in the rendering with a yellow and
flutter: black striped pattern. This is usually caused by the contents being too big for the RenderFlex.
flutter: Consider applying a flex factor (e.g. using an Expanded widget) to force the children of the
flutter: RenderFlex to fit within the available space instead of being sized to their natural size.
flutter: This is considered an error condition because it indicates that there is content that cannot be
flutter: seen. If the content is legitimately bigger than the available space, consider clipping it with a
flutter: ClipRect widget before putting it in the flex, or using a scrollable container rather than a Flex,
flutter: like a ListView.
flutter: The specific RenderFlex in question is:
flutter:   RenderFlex#094c9 OVERFLOWING
flutter:   creator: Column ← ScopedModelDescendant<EventModel> ← Semantics ← DefaultTextStyle ←
flutter:   AnimatedDefaultTextStyle ← _InkFeatures-[GlobalKey#5fe8b ink renderer] ←
flutter:   NotificationListener<LayoutChangedNotification> ← CustomPaint ← _ShapeBorderPaint ← PhysicalShape
flutter:   ← _MaterialInterior ← Material ← ⋯
flutter:   parentData: <none> (can use size)
flutter:   constraints: BoxConstraints(w=56.0, h=104.3)
flutter:   size: Size(56.0, 104.3)
flutter:   direction: vertical
flutter:   mainAxisAlignment: start
flutter:   mainAxisSize: max
flutter:   crossAxisAlignment: stretch
flutter:   verticalDirection: down
flutter: ◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤
flutter: ════════════════════════════════════════════════════════════════════════════════════════════════════

Solution

  • Wrap widget solves your problem. If there is not enough space to fit the child in a Column or Row, you can use Wrap. You can use alignment, directionality and spacing properties to customize it.

    Here is the simple example:

    class WrapExample extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return SizedBox(
          width: 200,
          height: 180,
          child: Card(
            clipBehavior: Clip.antiAlias,
            child: Wrap(
              direction: Axis.horizontal,
              spacing: 8.0, // gap between adjacent chips
              runSpacing: 4.0, // gap between lines
              children: <Widget>[
                Chip(
                  avatar: CircleAvatar(
                      backgroundColor: Colors.blue.shade900, child: Text('AH')),
                  label: Text('Hamilton'),
                ),
                Chip(
                  avatar: CircleAvatar(
                      backgroundColor: Colors.blue.shade900, child: Text('ML')),
                  label: Text('Lafayette'),
                ),
                Chip(
                  avatar: CircleAvatar(
                      backgroundColor: Colors.blue.shade900, child: Text('HM')),
                  label: Text('Mulligan'),
                ),
                Chip(
                  avatar: CircleAvatar(
                      backgroundColor: Colors.blue.shade900, child: Text('JL')),
                  label: Text('Laurens'),
                ),
              ],
            ),
          ),
        );
      }
    }
    

    And here is the output:

    Screenshot