Flutter - Using Wrap Widget Examples

This tutorial shows you how to use Wrap widget in Flutter.

In Flutter, Wrap is a widget that displays its children in multiple horizontal or vertical runs. For each child, it will try to place it next to the previous child in the main axis. If there is not enough space to place a child in the main axis, it will create a new run adjacent to its existing children in the cross axis. This widget can be useful if you need to place a list of items in multiple rows or columns.

Using Wrap Widget

Below is the constructor of Wrap.

  Wrap({
    Key key,
    this.direction = Axis.horizontal,
    this.alignment = WrapAlignment.start,
    this.spacing = 0.0,
    this.runAlignment = WrapAlignment.start,
    this.runSpacing = 0.0,
    this.crossAxisAlignment = WrapCrossAlignment.start,
    this.textDirection,
    this.verticalDirection = VerticalDirection.down,
    this.clipBehavior = Clip.hardEdge,
    List children = const [],
  })

For this tutorial, we are going to use the following methods for generating the children.

  Widget _generateItem(double width, double height) {
    return Container(
      width: width,
      height: height,
      decoration: BoxDecoration(
        border: Border.all(color: Colors.blueAccent),
        color: Colors.pink,
      ),
      child: const Center(
        child: const Text('Woolha', style: TextStyle(color: Colors.white)),
      ),
    );
  }
  
  List<Widget> _generateChildren(int count) {
    List<Widget> items = [];
    
    for (int i = 0; i < count; i++) {
      items.add(_generateItem(90, 75));
    }
    
    return items;
  }

In the first example, we only pass the children argument. That means the default alignment (horizontal) is used. By default, the width of the Wrap widget depends on its content. For example, if the direction is horizontal and each run can have 4 items each with a width of 90, the width of the Wrap widget will be 360 assuming there is no spacing between items. If you want to make it as wide as the parent, the solution is to set the Wrap widget as the child of another widget that can set the width constraint, such as SizedBox.

  SizedBox(
    width: double.infinity,
    child: Wrap(
      children: _generateChildren(7),
    ),
  )

Output:

Flutter - Wrap

That's the result of a basic example that only passes the children argument and leaves the other options to default. For the width of the Wrap widget, you may not see the effect of setting the width constraint from the above output because the default alignment is start. However, you can see that actually the width matches the parent width by using Flutter Inspector. It will be more obvious if the alignment type is not start which will be exemplified later in this tutorial

Set Direction

The default value for the direction argument is horizontal if we don't set it. That means Flutter will try to put an item in the same row or beside the previous item as long as the space in the same row is enough to place a new item, before moving to the next row if the space isn't enough. If you want to put the next item in the same column instead (before moving to the next column if the current column is already full), you can pass direction argument with Axis.vertical as the value.

  SizedBox(
    height: double.infinity,
    child: Wrap(
      direction: Axis.vertical,
      children: _generateChildren(7),
    ),
  )

Output:

Flutter - Wrap - direction vertical

Set Alignment

The alignment between children in the main axis can be set using alignment argument whose type is WrapAlignment. The possible values are:

  • start: Place the objects at the start of the main axis.
  • end Place the objects at the middle of the main axis.
  • center: Place the objects at the end of the main axis.
  • spaceBetween: Place the free space evenly between the objects.
  • spaceAround: Place the free space evenly between the objects as well as half of that space before and after the first and last objects.
  • spaceEvenly: Place the free space evenly between the objects as well as before and after the first and last objects.

The below example sets the alignment value to WrapAlignment.center.

  SizedBox(
    width: double.infinity,
    child: Wrap(
      alignment: WrapAlignment.center,
      children: _generateChildren(7),
    ),
  )

Output:

Flutter - Wrap - alignment - center

You can see the following images to see the differences between supported alignments.

start
Flutter - Wrap - alignment - start

end
Flutter - Wrap - alignment - end

center
Flutter - Wrap - alignment - center

spaceBetween
Flutter - Wrap - alignment - spaceBetween

spaceAround
Flutter - Wrap - alignment - spaceAround

spaceEvenly
Flutter - Wrap - alignment - spaceEvenly

Set Run Alignment

The runAlignment argument whose type is also WrapAlignment can be used to set how the runs should be placed in the cross axis. The list of possible values is the same as alignment.

Here's the usage example. Because this example uses default (horizontal) alignment,the height constraint for the Wrap widget should be bigger than the intrinsic height of the widget, so that we can see the differences of using the supported values for runAlignment.

  Container(
    width: double.infinity,
    height: 500,
    color: Colors.grey,
    child: Wrap(
      runAlignment: WrapAlignment.end,
      children: _generateChildren(7),
    ),
  )

Output:

Flutter - Wrap - runAlignment - center

The below images are the screenshots of using all supported values.

start
Flutter - Wrap - runAlignment - start

end
Flutter - Wrap - runAlignment - end

center
Flutter - Wrap - runAlignment - center

spaceBetween
Flutter - Wrap - runAlignment - spaceBetween

spaceAround
Flutter - Wrap - runAlignment - spaceAround

spaceEvenly
Flutter - Wrap - runAlignment - spaceEvenly

Set Cross Axis Alignment

For the cross alignment between children in a run, you can pass crossAxisAlignment argument whose type is WrapCrossAlignment. The available values are:

  • start: places the children as close to the start of the run in the cross axis.
  • end: places the children as close to the end of the run in the cross axis.
  • center: places the children as close to the middle of the run in the cross axis.

To make it obvious, the children must have different heights if the direction is horizontal. To achieve that, we are going to use the below function which generates children with different heights.

  List<Widget> _generateChildrenWithCustomHeight(int count) {
    List<Widget> items = [];

    for (int i = 0; i < count; i++) {
      items.add(_generateItem(90, 20.0 + (i % 3 * 10)));
    }

    return items;
  }

Below is an example with crossAxisAlignment sets to center.

  SizedBox(
    width: double.infinity,
    child: Wrap(
      crossAxisAlignment: WrapCrossAlignment.center,
      children: _generateChildrenWithCustomHeight(7),
    ),
  )

Output:

Flutter - Wrap - crossAxisAlignment - center

The below pictures show the differences between the supported crossAxisAlignment values.

start
Flutter - Wrap - crossAxisAlignment - start

end
Flutter - Wrap - crossAxisAlignment - end

center
Flutter - Wrap - crossAxisAlignment - center

Set Spacing

You can add main axis spacing between children in a run by passing spacing argument.

  SizedBox(
    width: double.infinity,
    child: Wrap(
      spacing: 10,
      children: _generateChildren(7),
    ),
  )

Output:

Flutter - Wrap - spacing - 10

Set Run Spacing

To set how much spacing between runs in the cross axis, you need to pass runSpacing argument.

  SizedBox(
    width: double.infinity,
    child: Wrap(
      runSpacing: 20,
      children: _generateChildren(7),
    ),
  )

Output:

Flutter - Wrap - runSpacing - 20

Set Text Direction

The textDirection argument can be used to define how to lay the children horizontally. The example below sets the directions to rtl (right-to-left).

  SizedBox(
    width: double.infinity,
    child: Wrap(
      textDirection: TextDirection.rtl,
      children: _generateChildren(7),
    ),
  )

Output:

Flutter - Wrap - textDirection - rtl

Wrap Parameters

  • Key key: The widget's key.
  • Axis direction: The direction to use as the main axis. Defaults to Axis.horizontal.
  • WrapAlignment alignment: How the children within a run should be placed in the main axis. Defaults to WrapAlignment.start.
  • double spacing: Amount of space between children in a run in the main axis.. Defaults to 0.0.
  • WrapAlignment runAlignment: How the runs should be placed in the cross axis. Defaults to WrapAlignment.start.
  • double runSpacing: Amount of space between the runs themselves in the cross axis. Defaults to 0.0.
  • WrapCrossAlignment crossAxisAlignment: How the children within a run should be aligned relative to each other in the cross axis. Defaults to WrapCrossAlignment.start.
  • TextDirection textDirection: The order to lay children out horizontally and how to interpret start and end in the horizontal direction.
  • Clip clipBehavior: How to content will be clipped. Defaults to VerticalDirection.down.
  • List<Widget> children: The widgets below this widget in the tree which are the list of items.. Defaults to const [].

Full Code

  import 'package:flutter/material.dart';
  
  void main() => runApp(MyApp());
  
  class MyApp extends StatelessWidget {
  
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'Woolha.com Flutter Tutorial',
        home: _WrapExample(),
      );
    }
  }
  
  class _WrapExample extends StatelessWidget {
  
    Widget _generateItem(double width, double height) {
      return Container(
        width: width,
        height: height,
        decoration: BoxDecoration(
          border: Border.all(color: Colors.blueAccent),
          color: Colors.pink,
        ),
        child: const Center(
          child: const Text('Woolha', style: TextStyle(color: Colors.white)),
        ),
      );
    }
    
    List<Widget> _generateChildren(int count) {
      List<Widget> items = [];
      
      for (int i = 0; i < count; i++) {
        items.add(_generateItem(90, 75));
      }
      
      return items;
    }
  
    List<Widget> _generateChildrenWithCustomHeight(int count) {
      List<Widget> items = [];
  
      for (int i = 0; i < count; i++) {
        items.add(_generateItem(90, 20.0 + (i % 3 * 10)));
      }
  
      return items;
    }
  
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: const Text('Woolha.com Flutter Tutorial'),
        ),
        body: Container(
          width: double.infinity,
          height: 500,
          color: Colors.grey,
          child: Wrap(
            direction: Axis.horizontal,
            alignment: WrapAlignment.spaceAround,
            runAlignment: WrapAlignment.spaceEvenly,
            crossAxisAlignment: WrapCrossAlignment.center,
            clipBehavior: Clip.antiAlias,
            spacing: 10,
            runSpacing: 20,
            textDirection: TextDirection.rtl,
            children: _generateChildrenWithCustomHeight(7),
          ),
        ),
      );
    }
  }

Summary

That's how to use Wrap widget in Flutter. It can be useful if you need a layout where the children should be placed in multiple horizontal and vertical runs. Setting the direction, alignment and spacing can be done easily by passing optional named arguments.