Overview

Flex is a widget that displays its children in a one-dimensional array.

The Flex widget allows you to control the axis along which the children are placed (horizontal or vertical). This is referred to as the main axis. If you know the main axis in advance, then consider using a Row (if it’s horizontal) or Column (if it’s vertical) instead, because that will be less verbose.

To cause a child to expand to fill the available space in the direction of this widget’s main axis, wrap the child in an Expanded widget.

The Flex widget does not scroll (and in general it is considered an error to have more children in a Flex than will fit in the available room). If you have some widgets and want them to be able to scroll if there is insufficient room, consider using a ListView.

See: https://api.flutter.dev/flutter/widgets/Flex-class.html

When to use it?

  • When you need dynamic control over direction rather than fixed Row or Column
  • When you need to change horizontal/vertical layout at runtime
  • When direction changes based on screen size in responsive design
  • When creating common layout components
  • As a foundation for complex layout systems

Basic Usage

// Horizontal direction (same as Row)
Flex({
  direction: Axis.horizontal,
  children: [
    Container({ width: 50, height: 50, color: 'red' }),
    Container({ width: 50, height: 50, color: 'green' }),
    Container({ width: 50, height: 50, color: 'blue' })
  ]
})

// Vertical direction (same as Column)
Flex({
  direction: Axis.vertical,
  children: [
    Container({ width: 100, height: 50, color: 'red' }),
    Container({ width: 100, height: 50, color: 'green' }),
    Container({ width: 100, height: 50, color: 'blue' })
  ]
})

// Dynamic direction
const [isHorizontal, setIsHorizontal] = useState(true);

Flex({
  direction: isHorizontal ? Axis.horizontal : Axis.vertical,
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: widgets
})

Props

direction (required)

Value: Axis

The direction along which children are laid out.

  • Axis.horizontal: Horizontal layout (same as Row)
  • Axis.vertical: Vertical layout (same as Column)
Flex({
  direction: Axis.horizontal,  // Horizontal layout
  children: children
})

Flex({
  direction: Axis.vertical,   // Vertical layout
  children: children
})

mainAxisAlignment

Value: MainAxisAlignment (default: MainAxisAlignment.start)

How children are aligned along the main axis.

  • MainAxisAlignment.start: Align at the start
  • MainAxisAlignment.end: Align at the end
  • MainAxisAlignment.center: Center alignment
  • MainAxisAlignment.spaceBetween: Place at ends with equal space between
  • MainAxisAlignment.spaceAround: Equal space around each child
  • MainAxisAlignment.spaceEvenly: Equal space distribution
Flex({
  direction: Axis.horizontal,
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: children
})

crossAxisAlignment

Value: CrossAxisAlignment (default: CrossAxisAlignment.center)

How children are aligned along the cross axis.

  • CrossAxisAlignment.start: Cross axis start
  • CrossAxisAlignment.end: Cross axis end
  • CrossAxisAlignment.center: Cross axis center
  • CrossAxisAlignment.stretch: Stretch along cross axis
  • CrossAxisAlignment.baseline: Align to text baseline
Flex({
  direction: Axis.horizontal,
  crossAxisAlignment: CrossAxisAlignment.stretch,
  children: children
})

verticalDirection

Value: VerticalDirection (default: VerticalDirection.down)

Specifies the vertical direction order.

  • VerticalDirection.down: Top to bottom
  • VerticalDirection.up: Bottom to top
Flex({
  direction: Axis.vertical,
  verticalDirection: VerticalDirection.up,  // Bottom to top
  children: children
})

mainAxisSize

Value: MainAxisSize (default: MainAxisSize.max)

How much space to occupy in the main axis direction.

  • MainAxisSize.max: Occupy maximum available space
  • MainAxisSize.min: Occupy only the minimum space needed for children

clipped

Value: boolean (default: false)

Whether to clip children when they overflow the Flex area.

children (required)

Value: Widget[]

The array of child widgets to layout.

Practical Examples

Example 1: Responsive Navigation Bar

const ResponsiveNavBar = ({ isDesktop }) => {
  return Container({
    padding: EdgeInsets.all(16),
    decoration: BoxDecoration({
      color: 'white',
      boxShadow: [
        BoxShadow({
          color: 'rgba(0,0,0,0.1)',
          blurRadius: 4,
          offset: { x: 0, y: 2 }
        })
      ]
    }),
    child: Flex({
      direction: isDesktop ? Axis.horizontal : Axis.vertical,
      mainAxisAlignment: isDesktop 
        ? MainAxisAlignment.spaceBetween 
        : MainAxisAlignment.start,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        Text('Logo', {
          style: TextStyle({
            fontSize: 24,
            fontWeight: 'bold'
          })
        }),
        Flex({
          direction: isDesktop ? Axis.horizontal : Axis.vertical,
          mainAxisSize: MainAxisSize.min,
          children: [
            NavItem({ title: 'Home', active: true }),
            NavItem({ title: 'About' }),
            NavItem({ title: 'Services' }),
            NavItem({ title: 'Contact' })
          ]
        })
      ]
    })
  });
};

Example 2: Dynamic Form Layout

const DynamicFormLayout = ({ layout, fields }) => {
  return Container({
    padding: EdgeInsets.all(16),
    child: Flex({
      direction: layout === 'horizontal' ? Axis.horizontal : Axis.vertical,
      crossAxisAlignment: layout === 'horizontal' 
        ? CrossAxisAlignment.start 
        : CrossAxisAlignment.stretch,
      children: fields.map((field, index) => {
        if (layout === 'horizontal') {
          return Expanded({
            child: Container({
              margin: EdgeInsets.only({ 
                right: index < fields.length - 1 ? 16 : 0 
              }),
              child: FormField({ field })
            })
          });
        } else {
          return Container({
            margin: EdgeInsets.only({ 
              bottom: index < fields.length - 1 ? 16 : 0 
            }),
            child: FormField({ field })
          });
        }
      })
    })
  });
};

Example 3: Card Action Buttons

const CardActions = ({ actions, vertical = false }) => {
  return Container({
    padding: EdgeInsets.all(16),
    child: Flex({
      direction: vertical ? Axis.vertical : Axis.horizontal,
      mainAxisAlignment: vertical 
        ? MainAxisAlignment.start 
        : MainAxisAlignment.end,
      mainAxisSize: MainAxisSize.min,
      children: actions.map((action, index) => 
        Container({
          margin: vertical 
            ? EdgeInsets.only({ bottom: index < actions.length - 1 ? 8 : 0 })
            : EdgeInsets.only({ left: index > 0 ? 8 : 0 }),
          child: ElevatedButton({
            onPressed: action.onPressed,
            child: Text(action.label)
          })
        })
      )
    })
  });
};

Example 4: Stats Dashboard

const StatsDashboard = ({ stats, orientation }) => {
  return Container({
    padding: EdgeInsets.all(24),
    child: Flex({
      direction: orientation === 'horizontal' ? Axis.horizontal : Axis.vertical,
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: stats.map(stat => 
        Expanded({
          child: Container({
            margin: EdgeInsets.all(8),
            padding: EdgeInsets.all(20),
            decoration: BoxDecoration({
              color: 'white',
              borderRadius: BorderRadius.circular(12),
              boxShadow: [
                BoxShadow({
                  color: 'rgba(0,0,0,0.05)',
                  blurRadius: 6,
                  offset: { x: 0, y: 2 }
                })
              ]
            }),
            child: Column({
              mainAxisSize: MainAxisSize.min,
              children: [
                Text(stat.value, {
                  style: TextStyle({
                    fontSize: 32,
                    fontWeight: 'bold',
                    color: stat.color
                  })
                }),
                SizedBox({ height: 8 }),
                Text(stat.label, {
                  style: TextStyle({
                    fontSize: 16,
                    color: '#666'
                  })
                })
              ]
            })
          })
        })
      )
    })
  });
};
const AdaptiveGallery = ({ images, screenWidth }) => {
  const direction = screenWidth > 768 ? Axis.horizontal : Axis.vertical;
  const itemsPerRow = screenWidth > 768 ? 3 : 1;
  
  const chunks = [];
  for (let i = 0; i < images.length; i += itemsPerRow) {
    chunks.push(images.slice(i, i + itemsPerRow));
  }
  
  return SingleChildScrollView({
    child: Flex({
      direction: Axis.vertical,
      children: chunks.map(chunk => 
        Container({
          margin: EdgeInsets.only({ bottom: 16 }),
          child: Flex({
            direction: direction,
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: chunk.map(image => 
              Expanded({
                child: Container({
                  margin: EdgeInsets.symmetric({ horizontal: 8 }),
                  child: AspectRatio({
                    aspectRatio: 1.0,
                    child: Image({
                      src: image.url,
                      objectFit: 'cover'
                    })
                  })
                })
              })
            )
          })
        })
      )
    })
  });
};

Flex vs Row/Column

When to use Flex?

// ✅ When dynamic direction is needed
Flex({
  direction: isPortrait ? Axis.vertical : Axis.horizontal,
  children: children
})

// ✅ When creating common components
const FlexibleLayout = ({ direction, children }) => {
  return Flex({ direction, children });
};

// ❌ For fixed directions, Row/Column are simpler
Row({ children })  // Instead of Flex({ direction: Axis.horizontal, children })
Column({ children })  // Instead of Flex({ direction: Axis.vertical, children })

Important Notes

  • Flex does not scroll, so consider ListView for overflow situations
  • Children do not wrap to multiple lines, so consider Wrap widget if needed
  • For single child, Align or Center may be more appropriate
  • Direction changes cause complete layout recalculation, consider performance
  • Expanded or Flexible only work on the axis matching Flex direction
  • Row: Fixed horizontal Flex (direction: Axis.horizontal)
  • Column: Fixed vertical Flex (direction: Axis.vertical)
  • Wrap: Layout that allows children to wrap to new lines
  • ListView: Scrollable linear layout
  • Expanded: Widget that expands within Flex
  • Flexible: Widget with flexible size within Flex