Padding, Margin, Column & Row
Padding widget vs EdgeInsets, Container margin vs padding, Column mainAxisAlignment, Row crossAxisAlignment, Expanded, Flexible, Spacer, and Wrap.
Padding Widget & EdgeInsets
The Padding widget adds space around a child. EdgeInsets provides multiple constructors for specifying padding values.
// All sides equal
Padding(
padding: const EdgeInsets.all(16),
child: Text('All sides 16px'),
)
// Horizontal and vertical
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
child: Text('Symmetric padding'),
)
// Individual sides
Padding(
padding: const EdgeInsets.only(top: 8, left: 16, bottom: 4),
child: Text('Specific sides'),
)
// Respond to device text scaling
Padding(
padding: EdgeInsets.fromLTRB(16, 8, 16, 0),
child: Text('From LTRB'),
)
// Using EdgeInsetsDirectional (respects RTL)
Padding(
padding: const EdgeInsetsDirectional.only(start: 16, end: 8),
child: Text('RTL-aware padding'),
)
Container: margin vs padding
margin adds space outside the container's border, while padding adds space inside (between the border and the child content).
Container(
margin: const EdgeInsets.all(16), // outside: space from surrounding widgets
padding: const EdgeInsets.all(12), // inside: space between border and child
decoration: BoxDecoration(
color: Colors.blue[50],
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.blue),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
width: double.infinity,
child: const Text('I have both margin and padding'),
)
// Common shorthand: wrap Text directly in Padding for no background
const Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text('No background needed'),
)
Column & mainAxisAlignment
Column stacks children vertically. mainAxisAlignment controls vertical distribution; crossAxisAlignment controls horizontal alignment.
// mainAxisAlignment options
Column(
mainAxisAlignment: MainAxisAlignment.center, // center vertically
// MainAxisAlignment.start – top (default)
// MainAxisAlignment.end – bottom
// MainAxisAlignment.spaceBetween – equal space between items
// MainAxisAlignment.spaceAround – equal space around items
// MainAxisAlignment.spaceEvenly – equal space including edges
crossAxisAlignment: CrossAxisAlignment.start,
// CrossAxisAlignment.center (default)
// CrossAxisAlignment.end
// CrossAxisAlignment.stretch – fill full width
children: [
Container(color: Colors.red, width: 80, height: 40),
Container(color: Colors.green, width: 120, height: 40),
Container(color: Colors.blue, width: 60, height: 40),
],
)
// Column with mainAxisSize to wrap content height
Column(
mainAxisSize: MainAxisSize.min, // shrink-wrap children
children: const [
Text('Line 1'),
Text('Line 2'),
],
)
Row & crossAxisAlignment
Row places children horizontally. mainAxisAlignment controls horizontal distribution; crossAxisAlignment controls vertical alignment.
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Icon(Icons.star, size: 32),
const Text('Flutter', style: TextStyle(fontSize: 20)),
ElevatedButton(
onPressed: () {},
child: const Text('Get Started'),
),
],
)
// Row with baseline alignment for mixed text sizes
Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: const [
Text('100', style: TextStyle(fontSize: 36, fontWeight: FontWeight.bold)),
SizedBox(width: 4),
Text('USD', style: TextStyle(fontSize: 14, color: Colors.grey)),
],
)
Expanded, Flexible & Spacer
Expanded forces a child to fill remaining space. Flexible allows a child to take available space but can be smaller. Spacer pushes siblings apart.
// Expanded – forces full remaining space
Row(
children: [
const Icon(Icons.label),
const SizedBox(width: 8),
Expanded(
child: Text(
'Very long text that would overflow without Expanded',
overflow: TextOverflow.ellipsis,
),
),
const Icon(Icons.chevron_right),
],
)
// Flexible flex ratios (2:1 split)
Row(
children: [
Expanded(
flex: 2, // takes 2/3 of space
child: Container(color: Colors.blue, height: 50),
),
Expanded(
flex: 1, // takes 1/3 of space
child: Container(color: Colors.red, height: 50),
),
],
)
// Flexible – can be smaller than available space
Row(
children: [
Flexible(
child: Text('Short'), // won't be forced to fill
),
Flexible(
child: Text('Also short'),
),
],
)
// Spacer – pushes items to opposite ends
Row(
children: [
const Text('Left'),
const Spacer(), // fills remaining space between
const Text('Right'),
],
)
// Spacer with flex
Row(
children: [
const Text('A'),
const Spacer(flex: 2),
const Text('B'),
const Spacer(flex: 1),
const Text('C'),
],
)
Wrap Widget
Wrap is like a Row or Column that automatically wraps overflowing children onto the next line. Great for tags, chips, and button groups.
// Wrap for tag chips
Wrap(
spacing: 8, // horizontal gap between children
runSpacing: 8, // vertical gap between lines
alignment: WrapAlignment.start,
// WrapAlignment.center, .end, .spaceAround, .spaceBetween, .spaceEvenly
children: [
'Flutter', 'Dart', 'Android', 'iOS', 'Web', 'Desktop',
].map((tag) => Chip(label: Text(tag))).toList(),
)
// Wrap with direction column (wraps into columns)
Wrap(
direction: Axis.vertical,
spacing: 4,
runSpacing: 16,
children: [
Container(width: 80, height: 30, color: Colors.red),
Container(width: 80, height: 30, color: Colors.green),
Container(width: 80, height: 30, color: Colors.blue),
],
)
// Wrap for responsive button row
Wrap(
spacing: 8,
runSpacing: 8,
children: [
ElevatedButton(onPressed: () {}, child: const Text('Save')),
OutlinedButton(onPressed: () {}, child: const Text('Cancel')),
TextButton(onPressed: () {}, child: const Text('Preview')),
],
)