Sachin Soma
Sachin Soma

Reputation: 3802

Space between Column's children in Flutter

I have a Column widget with two TextField widgets as children and I want to have some space between both of them.

I already tried mainAxisAlignment: MainAxisAlignment.spaceAround, but the result was not what I wanted.

Upvotes: 288

Views: 364881

Answers (30)

CopsOnRoad
CopsOnRoad

Reputation: 267404

There are many ways of doing it, I'm listing a few here.

  1. Use SizedBox and provide some height:

     Column(
       children: <Widget>[
         Widget1(),
         SizedBox(height: 10), // <-- Set height
         Widget2(),
       ],
     )
    
  2. Use Spacer

     Column(
       children: <Widget>[
         Widget1(),
         Spacer(), // <-- Spacer
         Widget2(),
       ],
     )
    
  3. Use Expanded

     Column(
       children: <Widget>[
         Widget1(),
         Expanded(child: SizedBox.shrink()), // <-- Expanded
         Widget2(),
       ],
     )
    
  4. Set mainAxisAlignment

     Column(
       mainAxisAlignment: MainAxisAlignment.spaceAround, // <-- alignments
       children: <Widget>[
         Widget1(),
         Widget2(),
       ],
     )
    
  5. Use Wrap

     Wrap(
       direction: Axis.vertical, 
       spacing: 20, // <-- Spacing between children
       children: <Widget>[
         Widget1(),
         Widget2(),
       ],
     )
    
  6. Use spacing attribute (Flutter 3.27+):

     Column(
       spacing: 10, // <-- Set fixed height between children
       children: <Widget>[
         Widget1(),
         Widget2(),
       ],
     )
    

Upvotes: 177

Vehan Hemsara
Vehan Hemsara

Reputation: 11

With Flutter 3.27, A new spacing parameter has been added to Row and Column widgets, allowing developers to specify space between child elements.

      Row(
         spacing: 10, // <-- New parameter
         children: const [
           Text('Hello'),
           Text('World'),
           Text('Dart'),
         ],
       )
      Column(
         spacing: 10, // <-- New parameter
         children: const [
           Text('Hi'),
           Text('Hello')
         ],
       )

Upvotes: 0

Pratik Lakhani
Pratik Lakhani

Reputation: 333

Starting from Flutter 3.27, you can use the spacing property in the Column widget to easily add uniform spacing between its children. This simplifies layout code and avoids the need for additional widgets like SizedBox.

Here's an example:

Column(
  spacing: 10, // <-- Add uniform spacing between children
  children: <Widget>[
    TextField(),
    TextField(),
  ],
)

Benefits of Column spacing:

  1. Concise and Clean: Reduces boilerplate code for adding spacing.
  2. Consistent: Ensures uniform spacing between all children.
  3. Lightweight: Integrated into the Column widget, no extra widgets required.

This is the recommended approach if you are using Flutter 3.27 or later. For earlier versions, you would need alternatives like SizedBox or Spacer.

Upvotes: 5

Azhar Ali
Azhar Ali

Reputation: 2436

Flutter extension can be beneficial here, and if we've too many children and want to add some padding between children, we can create an extension on Column. Here is an example

extension WidgetExtension on Widget {
   Widget paddingAll(double value) => Padding(padding: EdgeInsets.all(value), child: this);
   
   Widget padding(EdgeInsets padding) => Padding(padding: padding, child: this);    
}

extension ColumnExtension on Column {
   Column childrenPadding(EdgeInsets padding) {
        return Column(
          children: children.map((e) => e.padding(padding)).toList(),
        );
    }
}

Usage

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(AppStrings.settings)),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            SettingTile(
              title: AppStrings.darkMode,
              leading: Icon(LineIcons.sun, color: context.primary),
            ),
            SettingTile(
              title: AppStrings.language,
              leading: Icon(LineIcons.language, color: context.primary),
            ),
            SettingTile(
              title: AppStrings.language,
              leading: Icon(LineIcons.language, color: context.primary),
            ),
            SettingTile(
              title: AppStrings.language,
              leading: Icon(LineIcons.language, color: context.primary),
            ),
          ],
        ).childrenPadding(const EdgeInsets.only(bottom: 8.0)),
      ),
    );
  }
}

Result

Column Extension example

Upvotes: 5

Abu Hurairah
Abu Hurairah

Reputation: 179

Bismillahir-Rahmanir-Rahim


Use interspersion.


Row(
    children: [
        Text("Element 1"),
        Text("Element 2"),
        Text("Element 3"),
    ]
    .map((e) => [SizedBox(width: 10), e])
    .expand((e) => e)
    .skip(1)
    .toList(),
),

Read: https://abuhurayrah.hashnode.dev/one-line-trick-to-intersperse-a-dart-list

Upvotes: -1

Mustafa Khan
Mustafa Khan

Reputation: 1

//add padding to expanded
Expanded(
            child: Padding(
              padding: const EdgeInsets.all(8.0),
              child: child1,
            ),),

          Expanded(
            child: Padding(
              padding: const EdgeInsets.all(8.0),
              child: child2,
            ),

Upvotes: 0

Praxder
Praxder

Reputation: 2749

Here's a simple subclass to do this:

class SpacedColumn extends Column {
  final double spacing;

  SpacedColumn({
    required this.spacing,
    super.key,
    super.mainAxisAlignment,
    super.mainAxisSize,
    super.crossAxisAlignment,
    super.textDirection,
    super.verticalDirection,
    super.textBaseline,
    super.children,
  }) {
    final spacedChildren = children.expand((final child) => [child, SizedBox(height: spacing)]).toList()..removeLast();
    children
      ..clear()
      ..addAll(spacedChildren);
  }
}

And now you can just create a SpacedColumn, just like a Column, but with a new spacing property. Example:

SpacedColumn(
  spacing: 15,
  children: [
    Text('First cell'),
    Text('Second cell'),
  ],
)

Upvotes: 1

Tianze Ds
Tianze Ds

Reputation: 81

A package called spaces2 can be used for this purpose.

By the way below, you can easily implement spaces between objects in columns or rows.

// Without Gap
Column(children: [x,y,z]);
// With Gaps Added
SpacedColumn(spaceBetween: 10, children: [x,y,z]);

Upvotes: 1

Matteo
Matteo

Reputation: 219

Basically all of the above answers are totally fine for small pieces of UI but if you are looking for a reusable and clean way to place a separator widget inside of a column, this might be what you are looking for:

class SeparatedColumn extends Column {
  SeparatedColumn({
    Key? key,
    MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
    MainAxisSize mainAxisSize = MainAxisSize.max,
    CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
    TextDirection? textDirection,
    VerticalDirection verticalDirection = VerticalDirection.down,
    TextBaseline? textBaseline,
    Widget? separator,
    required List<Widget> children,
  }) : super(
          key: key,
          children: List.generate(
            children.length * 2 - 1,
            (index) => index % 2 == 0
                ? children[index ~/ 2]
                : separator ?? const SizedBox.shrink(),
          ),
          mainAxisAlignment: mainAxisAlignment,
          mainAxisSize: mainAxisSize,
          crossAxisAlignment: crossAxisAlignment,
          textDirection: textDirection,
          verticalDirection: verticalDirection,
          textBaseline: textBaseline,
        );
}

Upvotes: 1

kynnysmatto
kynnysmatto

Reputation: 3892

I also wish there was some built-in way in Flutter to do this. Like a parameter you could pass to Column or Row. Sometimes you don't want padding around every element but you want space between them. Especially if you have more than two children, it's kind of tedious to write something like

const double gap = 10;
return Column(
  children: [
    Text('Child 1'),
    SizedBox(height: gap),
    Text('Child 2'),
    SizedBox(height: gap),
    Text('Child 3'),
    SizedBox(height: gap),
    Text('Child 4'),
  ],
);

However, I've came up with one quick (not perfect) solution:

Add this somewhere in your project (only once):

extension ListSpaceBetweenExtension on List<Widget> {
  List<Widget> withSpaceBetween({double? width, double? height}) => [
    for (int i = 0; i < length; i++)
      ...[
        if (i > 0)
          SizedBox(width: width, height: height),
        this[i],
      ],
  ];
}

And from now on whenever you have a Row or a Column, you can write

Column(
  children: [
    Text('Child 1'),
    Text('Child 2'),
    Text('Child 3'),
    Text('Child 4'),
  ].withSpaceBetween(height: 10),
),

When using Row you'll have to replace 'height' with 'width'.

Upvotes: 28

James Allen
James Allen

Reputation: 7149

Another alternative I'm surprised isn't listed here is to simply use ListView.separated. I use this quite a lot for lists, often using const Divider() as a material-style separator line for the spacer.

List<Widget> yourListOfWidgets = [
  const Text("Foo"),
  const Text("Bar"),
];
    
return ListView.separated(
  itemCount: yourListOfWidgets.length,
  itemBuilder: (context, index) => yourListOfWidgets[index],
  separatorBuilder: (context, index) => const SizedBox(height: 10),
)

Upvotes: 1

Ram Shukla
Ram Shukla

Reputation: 21

**// provide fixed space defined in sizedbox**


 Column(
     children: [
          widget1(),
          sizedbox(height:20), 
          widget2()
        ] 
     )

**// Provides equal space between childs** 

Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
     widget1(),
     widget2()
   ] 
)

Upvotes: 1

Nasser Alkhateeb
Nasser Alkhateeb

Reputation: 133

Adding multiple sized boxes gets tedious if you have too many children in a column, row, listView, etc., and if you have to do the same thing in many places in your application.

You can use the following utils to encapsulate the logic of adding these sized boxes:

List<Widget> addHorizontalSpaceBetweenElements(double space, List<Widget> widgets) {
  return _addSpaceBetweenElements(widgets, () => SizedBox(width: space));
}

List<Widget> addVerticalSpaceBetweenElements(double space, List<Widget> widgets) {
  return _addSpaceBetweenElements(widgets, () => SizedBox(height: space));
}

List<Widget> _addSpaceBetweenElements(List<Widget> widgets, ValueGetter<SizedBox> sizedBoxGetter) {
  if (widgets.isEmpty || widgets.length == 1) {
    return widgets;
  }
  List<Widget> spacedWidgets = [widgets.first];
  for (var i = 1; i < widgets.length - 1; i++) {
    spacedWidgets.add(sizedBoxGetter());
    spacedWidgets.add(widgets[i]);
  }
  spacedWidgets.add(sizedBoxGetter());
  spacedWidgets.add(widgets.last);
  return spacedWidgets;
}


And you use them like this:

Column(
  children: addVerticalSpaceBetweenElements(5, [
    Text(),
    Text(),
  Row(children: addHorizontalSpaceBetweenElements(5, [
    Text(),
    Text(),
  ])),
  ]));

Upvotes: 1

Yang_____
Yang_____

Reputation: 213

Inspired by https://stackoverflow.com/a/70993832/14298786, use extension on List<Widget> to add the SizedBox:

extension on List<Widget> {
  List<Widget> insertBetweenAll(Widget widget) {
    var result = List<Widget>.empty(growable: true);
    for (int i = 0; i < length; i++) {
      result.add(this[i]);
      if (i != length - 1) {
        result.add(widget);
      }
    }
    return result;
  }
}

Use like this:

Column(children: [
  Widget1(),
  Widget2(),
  Widget3(),
].insertBetweenAll(SizedBox(height: 20)))

Upvotes: 4

knary
knary

Reputation: 311

Extract the input field widgets into a custom widget that is wrapped in padding or a container with padding (assuming symmetrical spacing).

Having sized boxes in-between every column child (as suggested in other answers) is not practical or maintainable. If you wanted to change the spacing you would have to change each sized box widget.

// An input field widget as an example column child
class MyCustomInputWidget extends StatelessWidget {
  const MyCustomInputWidget({Key? key})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    // wrapping text field in container
    return Container(
      // here is the padding :)
      padding: EdgeInsets.symmetric(vertical: 10),
      child: TextField(...)
    );
  }
}

...then the column in the parent class

column(
  children: <Widget>[
    MyCustomInputWidget(),
    SizedBox(height: 10),
    MyCustomInputWidget(),
  ],
),

Obviously you would want the custom widget to have some sort of constructor to handle different field parameters.

Upvotes: 0

Shirsh Shukla
Shirsh Shukla

Reputation: 5973

Column widget doesn't have its own height, it just expanded as we added inside the children widget. for if you need the same space between multiple widgets so,

Container(
   width: double.infinity,
   height: height,
   child: Column(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: <Widget>[
         Text('One'),
         Text('Two'),
         Text('Three'),
         Text('Four')
      ],
   ),
),

or just added custom space between them by using SizeBox, Spacer, or widget, like this

Column(
   children: <Widget>[
         Text('One'),
         SizedBox(height: 20), 
         Text('Two'),
         SizedBox(height: 40), 
         Text('Three'),
         SizedBox(height: 30), 
         Text('Four')
   ],
 ),

Upvotes: 2

Sagnik Bhattacharya
Sagnik Bhattacharya

Reputation: 19

It's best to use the Wrap widget instead of a column or row.

Wrap( spacing: 10, runSpacing: 10, children:[], )

Upvotes: -1

Unes
Unes

Reputation: 70

If you don't wanna wrap Padding with every widget or repeat SizedBox.

Try this:

Column(
        children: [
          Widget(),
          Widget(),
          Widget(),
          Widget(),
        ]
            .map((e) => Padding(
                  child: e,
                  padding: const EdgeInsets.symmetric(vertical: 10),
                ))
            .toList(),
      ),

That will warp all the widgets with padding without repetition.

Upvotes: 5

jqgsninimo
jqgsninimo

Reputation: 7028

You can use Padding to wrap each child widget, and then set the top or bottom of Padding.

If you don’t want to write a lot of the same numbers, you can do it like this:

Column(
  children: [
    child1, 
    child2, 
    ..., 
    childN
  ].map((e) => Padding(padding: EdgeInsets.only(top: 10), child: e)).toList()
);

Upvotes: -1

fefe
fefe

Reputation: 19

You can try this :


import 'package:flutter/material.dart';

class CustomColumn extends Column {
  CustomColumn({
    Key? key,
    MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
    MainAxisSize mainAxisSize = MainAxisSize.max,
    CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
    TextDirection? textDirection,
    VerticalDirection verticalDirection = VerticalDirection.down,
    TextBaseline? textBaseline,
    List children = const [],
    EdgeInsetsGeometry? rowPadding,
  }) : super(
          children: children.map((e) => Padding(padding : rowPadding ?? EdgeInsets.only(bottom:12), child : e)).toList(),
          key: key,
          mainAxisAlignment: mainAxisAlignment,
          mainAxisSize: mainAxisSize,
          crossAxisAlignment: crossAxisAlignment,
          textDirection: textDirection,
          verticalDirection: verticalDirection,
          textBaseline: textBaseline,
        );
}

and call


CustomColumn(children: [
                    item1,
                    item2,
                    item3,
                  ])

Upvotes: -1

Antis
Antis

Reputation: 1

Here's another option involving a for loop.

Column(
  children: <Widget>[
    for (var i = 0; i < widgets.length; i++)
      Column(
        children: [
          widgets[i], // The widget you want to create or place goes here.
          SizedBox(height: 10) // Any kind of padding or other widgets you want to put.
        ])
  ],
),

Upvotes: -1

eeqk
eeqk

Reputation: 3862

I don't see this solution here, so just for the sake of completeness I'll post it.

You can also wrap children with Padding using map:

Column(
      children: [Text('child 1'), Text('child 2')]
          .map(
            (e) => Padding(
              padding: const EdgeInsets.all(8),
              child: e,
            ),
          )
          .toList(),
    );

Upvotes: 28

refik
refik

Reputation: 456

You can also use a helper function to add spacing after each child.

List<Widget> childrenWithSpacing({
  @required List<Widget> children,
  double spacing = 8,
}) {
  final space = Container(width: spacing, height: spacing);
  return children.expand((widget) => [widget, space]).toList();
}

So then, the returned list may be used as a children of a column

Column(
  children: childrenWithSpacing(
    spacing: 14,
    children: [
      Text('This becomes a text with an adjacent spacing'),
      if (true == true) Text('Also, makes it easy to add conditional widgets'),
    ],
  ),
);

I'm not sure though if it's wrong or have a performance penalty to run the children through a helper function for the same goal?

Upvotes: 2

Mendax
Mendax

Reputation: 11

The sized box will not help in the case, the phone is in landscape mode.

body: Column(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: <Widget>[
        Expanded(
           child: Container(
            margin: EdgeInsets.all(15.0),
            decoration: BoxDecoration(
              color: Color(0xFF1D1E33),
              borderRadius: BorderRadius.circular(10.0),
            ),
          ),
        ),
        Expanded(
           child: Container(
            margin: EdgeInsets.all(15.0),
            decoration: BoxDecoration(
              color: Color(0xFF1D1E33),
              borderRadius: BorderRadius.circular(10.0),
            ),
          ),
        ),
        Expanded(
           child: Container(
            margin: EdgeInsets.all(15.0),
            decoration: BoxDecoration(
              color: Color(0xFF1D1E33),
              borderRadius: BorderRadius.circular(10.0),
            ),
          ),
        ),
      ],
     )

Upvotes: -1

Issa
Issa

Reputation: 57

You may have to use SizedBox() widget between your column's children. Hope that'll be usefull

Upvotes: 0

Mahdi Tohidloo
Mahdi Tohidloo

Reputation: 3388

you can use Wrap() widget instead Column() to add space between child widgets.And use spacing property to give equal spacing between children

Wrap(
  spacing: 20, // to apply margin in the main axis of the wrap
  runSpacing: 20, // to apply margin in the cross axis of the wrap
  children: <Widget>[
     Text('child 1'),
     Text('child 2')
  ]
)

Upvotes: 161

Ali Esfandiari
Ali Esfandiari

Reputation: 329

Columns Has no height by default, You can Wrap your Column to the Container and add the specific height to your Container. Then You can use something like below:

Container(
   width: double.infinity,//Your desire Width
   height: height,//Your desire Height
   child: Column(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: <Widget>[
         Text('One'),
         Text('Two')
      ],
   ),
),

Upvotes: 4

Viren V Varasadiya
Viren V Varasadiya

Reputation: 27137

You can use Padding widget in between those two widget or wrap those widgets with Padding widget.

Update

SizedBox widget can be use in between two widget to add space between two widget and it makes code more readable than padding widget.

Ex:

Column(
  children: <Widget>[
    Widget1(),
    SizedBox(height: 10),
    Widget2(),
  ],
),

Upvotes: 348

Bhargav Sejpal
Bhargav Sejpal

Reputation: 1598

Column(children: <Widget>[
   Container(margin: EdgeInsets.only(top:12, child: yourWidget)),
   Container(margin: EdgeInsets.only(top:12, child: yourWidget))
]);

Upvotes: 1

Marcel
Marcel

Reputation: 9569

You can put a SizedBox with a specific height between the widgets, like so:

Column(
  children: <Widget>[
    FirstWidget(),
    SizedBox(height: 100),
    SecondWidget(),
  ],
),

Why to prefer this over wrapping the widgets in Padding? Readability! There is less visual boilerplate, less indention and the code follows the typical reading-order.

Upvotes: 177

Related Questions