HStack
A horizontal stack layout component that arranges its children horizontally with configurable spacing and alignment.
Overview
HStack provides horizontal layout capabilities using a custom RenderBox implementation. It demonstrates how Flutter’s layout system works under the hood, including support for flexible children via HCustomFlexible and HCustomExpanded.
Primitives Used: Custom RenderBox, CustomMultiChildLayout, ParentDataWidget
Basic Usage
import 'package:primitive_ui/primitive_ui.dart';
HStack(
spacing: 16.0,
children: [
Text('Fixed Width'),
HCustomExpanded(
child: Container(color: Colors.blue),
),
Text('Fixed Width'),
],
)API Reference
Constructor
HStack({
Key? key,
required List<Widget> children,
double spacing = 0.0,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.start,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
MainAxisSize mainAxisSize = MainAxisSize.max,
})Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
children | List<Widget> | ✓ | — | The widgets to display in the horizontal stack |
spacing | double | ✗ | 0.0 | Horizontal spacing between children (must be >= 0) |
crossAxisAlignment | CrossAxisAlignment | ✗ | CrossAxisAlignment.start | How to align children vertically |
mainAxisAlignment | MainAxisAlignment | ✗ | MainAxisAlignment.start | How to align children horizontally |
mainAxisSize | MainAxisSize | ✗ | MainAxisSize.max | Whether to take maximum or minimum horizontal space |
HCustomFlexible & HCustomExpanded
Wrap children in these widgets to control their flex behavior.
HCustomFlexible
HCustomFlexible({
Key? key,
int flex = 1,
FlexFit fit = FlexFit.loose,
required Widget child,
})HCustomExpanded
Shortcut for HCustomFlexible(fit: FlexFit.tight).
HCustomExpanded({
Key? key,
int flex = 1,
required Widget child,
})Examples
Different Alignments
Top
HStack(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8.0,
children: [
Container(width: 50, height: 100, color: Colors.red),
Container(width: 50, height: 150, color: Colors.green),
Container(width: 50, height: 80, color: Colors.blue),
],
)Flexible Layouts
Expanded
// Fills available horizontal space
HStack(
children: [
Container(width: 50, color: Colors.red),
HCustomExpanded(
child: Container(color: Colors.blue), // Takes remaining space
),
Container(width: 50, color: Colors.green),
],
)Main Axis Alignment
Start
HStack(
mainAxisAlignment: MainAxisAlignment.start,
spacing: 8.0,
children: [
Container(width: 50, height: 50, color: Colors.red),
Container(width: 50, height: 50, color: Colors.green),
Container(width: 50, height: 50, color: Colors.blue),
],
)Toolbar Layout
PrimitiveCard(
child: HStack(
spacing: 16.0,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Dashboard',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
HStack(
spacing: 8.0,
children: [
IconButton(icon: Icon(Icons.search), onPressed: () {}),
IconButton(icon: Icon(Icons.notifications), onPressed: () {}),
CircleAvatar(child: Text('U')),
],
),
],
),
)Implementation Details
Layout Algorithm
The HStack layout process has been upgraded to support two-pass layout for flex children:
-
Pass 1 (Fixed Children): Measure all non-flex children.
if (flex == 0) { child.layout(...); totalNonFlexWidth += child.size.width; } -
Calculate Free Space: Determine space available for flex children.
freeSpace = availableWidth - totalNonFlexWidth - totalSpacing; -
Pass 2 (Flex Children): Measure flex children using allocated space.
childWidth = (freeSpace * flex) / totalFlex; child.layout(constraints.tighten(width: childWidth)); -
Positioning: Place all children in sequence.
RTL Support
HStack respects text direction for horizontal positioning:
if (textDirection == TextDirection.rtl) {
currentX = width - leadingSpace;
// ...
currentX -= child.size.width;
// ...
currentX -= betweenSpace;
} else {
currentX = leadingSpace;
// ...
currentX += child.size.width + betweenSpace;
}Intrinsic Dimensions
HStack implements all intrinsic dimension methods:
computeMinIntrinsicWidth: Sum of children’s min widths + spacingcomputeMaxIntrinsicWidth: Sum of children’s max widths + spacingcomputeMinIntrinsicHeight: Maximum of children’s min heightscomputeMaxIntrinsicHeight: Maximum of children’s max heights
Common Patterns
Disabled State
// This is an example from PrimitiveToggleSwitch. Replace with relevant HStack example.
bool _isDisabled = true;
HStack(
children: [
Text('Item 1'),
Container(width: 20, height: 20, color: Colors.grey),
Text('Item 2'),
],
)Differences from Row
| Feature | HStack | Row |
|---|---|---|
| Flexible/Expanded | ✅ Supported (via HCustomFlexible) | ✅ Supported |
| Baseline alignment | ❌ Not supported | ✅ Supported |
| Text direction | ✅ Supported | ✅ Supported |
| Intrinsic sizing | ✅ Implemented | ✅ Implemented |
| Performance | Good for simple layouts | Optimized for all cases |
Best Practices
Use HStack When:
- You need simple horizontal layouts
- Learning how layout systems work
- Building educational projects
- Spacing between children is uniform
- You need precise control over spacing (using
spacingparam)
Use Row Instead When:
- You need baseline alignment
- Performance is critical with many children
- You rely on standard Flutter widgets that expect
Row
Related Components
Source Code
View the complete implementation in the GitHub repository .