Overview
Align is a widget that aligns its child within itself and optionally sizes itself based on the child’s size. It uses a normalized coordinate system to flexibly position the child.
Reference: https://api.flutter.dev/flutter/widgets/Align-class.html
When to Use
- When you need to align a child widget at a specific position within its parent
- When creating specific ratio margins around a child widget
- When center-aligning content in dialogs or popups
- When positioning buttons or icons at specific locations
- When adjusting parent size proportionally to the child widget’s size
Basic Usage
// Center alignment (default)
Align({
child: Text('Centered text')
})
// Top-right alignment
Align({
alignment: Alignment.topRight,
child: Icon(Icons.close)
})
// Using size factors
Align({
widthFactor: 2.0,
heightFactor: 1.5,
child: Container({
width: 100,
height: 100,
color: 'blue'
})
})
Props
child (optional)
Type: Widget | undefined
The child widget to align.
alignment (optional)
Type: Alignment (default: Alignment.center)
Specifies the alignment position of the child widget. Alignment consists of x and y values, each ranging from -1.0 to 1.0.
Main Alignment constants:
Alignment.topLeft
: (-1.0, -1.0)Alignment.topCenter
: (0.0, -1.0)Alignment.topRight
: (1.0, -1.0)Alignment.centerLeft
: (-1.0, 0.0)Alignment.center
: (0.0, 0.0)Alignment.centerRight
: (1.0, 0.0)Alignment.bottomLeft
: (-1.0, 1.0)Alignment.bottomCenter
: (0.0, 1.0)Alignment.bottomRight
: (1.0, 1.0)
// Custom alignment
Align({
alignment: Alignment.of({ x: 0.5, y: -0.5 }), // Towards top-right
child: Text('Custom alignment')
})
widthFactor (optional)
Type: number | undefined
Sets this widget’s width as a multiple of the child widget’s width. If null, expands to available width.
Align({
widthFactor: 1.5, // 1.5 times child's width
child: Container({
width: 100,
height: 50,
color: 'red'
})
})
// Align's width becomes 150
heightFactor (optional)
Type: number | undefined
Sets this widget’s height as a multiple of the child widget’s height. If null, expands to available height.
Align({
heightFactor: 2.0, // 2 times child's height
child: Container({
width: 100,
height: 50,
color: 'green'
})
})
// Align's height becomes 100
Layout Behavior
Size Constraint Handling
-
Under bounded constraints:
- If widthFactor and heightFactor are null, expands to available size
- Layouts child with loosened constraints
- Positions child according to alignment
-
Under unbounded constraints:
- If widthFactor and heightFactor are null, shrinks to child size
- If factors are specified, size is determined by child size × factor
-
When using size factors:
- Always sizes to child size × factor
- Works independently of parent constraints
Practical Examples
Example 1: Close Button Alignment
const DialogWithCloseButton = ({ title, content, onClose }) => {
return Container({
padding: EdgeInsets.all(20),
decoration: BoxDecoration({
color: 'white',
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow({
color: 'rgba(0, 0, 0, 0.1)',
blurRadius: 10,
offset: { x: 0, y: 4 }
})
]
}),
child: Stack({
children: [
Column({
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, {
style: TextStyle({
fontSize: 20,
fontWeight: 'bold'
})
}),
SizedBox({ height: 12 }),
Text(content)
]
}),
Align({
alignment: Alignment.topRight,
child: GestureDetector({
onTap: onClose,
child: Icon(Icons.close, { size: 24 })
})
})
]
})
});
};
Example 2: Centered Loading Indicator
const LoadingOverlay = ({ isLoading, child }) => {
return Stack({
children: [
child,
if (isLoading) Container({
color: 'rgba(0, 0, 0, 0.5)',
child: Align({
alignment: Alignment.center,
child: Container({
padding: EdgeInsets.all(20),
decoration: BoxDecoration({
color: 'white',
borderRadius: BorderRadius.circular(8)
}),
child: Column({
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(),
SizedBox({ height: 16 }),
Text('Loading...')
]
})
})
})
})
]
});
};
Example 3: Badge Positioning
const IconWithBadge = ({ icon, badgeCount }) => {
return Container({
width: 48,
height: 48,
child: Stack({
children: [
Center({
child: Icon(icon, { size: 32 })
}),
if (badgeCount > 0) Align({
alignment: Alignment.topRight,
child: Container({
padding: EdgeInsets.all(4),
decoration: BoxDecoration({
color: 'red',
shape: BoxShape.circle
}),
constraints: BoxConstraints({
minWidth: 20,
minHeight: 20
}),
child: Center({
child: Text(badgeCount.toString(), {
style: TextStyle({
color: 'white',
fontSize: 12,
fontWeight: 'bold'
})
})
})
})
})
]
})
});
};
Example 4: Tooltip Alignment
class TooltipWidget extends StatefulWidget {
message: string;
child: Widget;
alignment: Alignment;
constructor({ message, child, alignment = Alignment.topCenter }) {
super();
this.message = message;
this.child = child;
this.alignment = alignment;
}
createState(): State<TooltipWidget> {
return new TooltipWidgetState();
}
}
class TooltipWidgetState extends State<TooltipWidget> {
isVisible = false;
build(): Widget {
return Stack({
clipBehavior: Clip.none,
children: [
GestureDetector({
onMouseEnter: () => {
this.setState(() => {
this.isVisible = true;
});
},
onMouseLeave: () => {
this.setState(() => {
this.isVisible = false;
});
},
child: this.widget.child
}),
if (this.isVisible) Align({
alignment: this.widget.alignment,
widthFactor: 1.0,
child: Transform.translate({
offset: { x: 0, y: this.widget.alignment.y < 0 ? -30 : 30 },
child: Container({
padding: EdgeInsets.symmetric({ horizontal: 12, vertical: 6 }),
decoration: BoxDecoration({
color: 'rgba(0, 0, 0, 0.8)',
borderRadius: BorderRadius.circular(4)
}),
child: Text(this.widget.message, {
style: TextStyle({
color: 'white',
fontSize: 14
})
})
})
})
})
]
});
}
}
Example 5: Responsive Card Layout
const ResponsiveCard = ({ title, subtitle, action }) => {
return Container({
padding: EdgeInsets.all(16),
decoration: BoxDecoration({
border: Border.all({ color: '#E0E0E0' }),
borderRadius: BorderRadius.circular(8)
}),
child: Row({
children: [
Expanded({
child: Column({
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, {
style: TextStyle({
fontSize: 18,
fontWeight: 'bold'
})
}),
SizedBox({ height: 4 }),
Text(subtitle, {
style: TextStyle({
color: '#666',
fontSize: 14
})
})
]
})
}),
Align({
alignment: Alignment.centerRight,
widthFactor: 1.2,
child: action
})
]
})
});
};
Important Notes
- widthFactor and heightFactor must be greater than 0
- alignment x and y values must be between -1.0 and 1.0
- When there’s no child and widthFactor or heightFactor is null, size may become 0
- Understand the difference between Align and Positioned when using within Stack
- Using size factors can impact layout performance
- Loosens constraints to allow the child to have its desired size
Related Widgets
- Center: Special case of Align, always centers content
- Positioned: Absolute positioning within Stack
- Container: Can align children using alignment property
- FractionallySizedBox: Sizes based on parent’s dimensions ratio
- AnimatedAlign: Animated transitions between alignments