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 startMainAxisAlignment.end
: Align at the endMainAxisAlignment.center
: Center alignmentMainAxisAlignment.spaceBetween
: Place at ends with equal space betweenMainAxisAlignment.spaceAround
: Equal space around each childMainAxisAlignment.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 startCrossAxisAlignment.end
: Cross axis endCrossAxisAlignment.center
: Cross axis centerCrossAxisAlignment.stretch
: Stretch along cross axisCrossAxisAlignment.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 bottomVerticalDirection.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 spaceMainAxisSize.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'
})
})
]
})
})
})
)
})
});
};
Example 5: Adaptive Image Gallery
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
Related Widgets
- 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