Box Model
Understand the box model and how Flutter handles spacing and sizing.
What is it?
The box model in Flutter is the system for managing the space around and within widgets. It includes padding (space inside the box), margin (space outside the box), and the content itself. Understanding the box model helps you control spacing, alignment, and sizing in your layouts.
Why does it exist?
The box model exists to:
- Control spacing between widgets
- Manage internal and external spacing
- Create consistent layouts
- Handle sizing and constraints
- Support responsive design
- Enable visual hierarchy
- Provide flexible layout options
The Box Model Components
The box model consists of four main components.
┌─────────────────────────────────────┐
│ Margin │
│ ┌─────────────────────────────────┐ │
│ │ Border │ │
│ │ ┌───────────────────────────┐ │ │
│ │ │ Padding │ │ │
│ │ │ ┌─────────────────────┐ │ │ │
│ │ │ │ Content │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ └─────────────────────┘ │ │ │
│ │ └───────────────────────────┘ │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────┘
// Components:
// 1. Content - The actual widget content
// 2. Padding - Space inside the box
// 3. Border - Border around the box
// 4. Margin - Space outside the box
What's happening here? - Content is the innermost element - Padding adds space inside the border - Border wraps around padding and content - Margin adds space outside the border - All components affect layout
Container and Box Model
Container implements the full box model.
// Container with full box model
class ContainerBoxModel extends StatelessWidget {
const ContainerBoxModel({super.key});
@override
Widget build(BuildContext context) {
return Container(
// 1. Margin - Space outside
margin: const EdgeInsets.all(20),
// 2. Padding - Space inside
padding: const EdgeInsets.all(16),
// 3. Decoration - Border and background
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(
color: Colors.black,
width: 3,
),
borderRadius: BorderRadius.circular(10),
),
// 4. Content
child: const Text(
'Hello World',
style: TextStyle(color: Colors.white),
),
);
}
}
// Visual breakdown:
// margin: 20px outside
// border: 3px border
// padding: 16px inside
// content: text
// Total space: margin + border + padding + content
What's happening here? - Container provides full box model - Margin creates external spacing - Padding creates internal spacing - Decoration adds border and background - Child is the content
EdgeInsets
EdgeInsets controls padding and margin.
// EdgeInsets variations
class EdgeInsetsExample extends StatelessWidget {
const EdgeInsetsExample({super.key});
@override
Widget build(BuildContext context) {
return Column(
children: [
// 1. All sides same
Container(
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.all(8),
color: Colors.blue,
child: const Text('All sides'),
),
// 2. Individual sides
Container(
margin: const EdgeInsets.only(
left: 10,
right: 20,
top: 5,
bottom: 15,
),
padding: const EdgeInsets.fromLTRB(10, 5, 20, 15),
color: Colors.red,
child: const Text('Individual sides'),
),
// 3. Symmetric (horizontal/vertical)
Container(
margin: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 10,
),
padding: const EdgeInsets.symmetric(
horizontal: 15,
vertical: 5,
),
color: Colors.green,
child: const Text('Symmetric'),
),
// 4. Only specific sides
Container(
margin: const EdgeInsets.only(
left: 20,
right: 20,
),
padding: const EdgeInsets.only(
top: 10,
bottom: 10,
),
color: Colors.orange,
child: const Text('Only specific sides'),
),
// 5. Zero (no spacing)
Container(
margin: EdgeInsets.zero,
padding: EdgeInsets.zero,
color: Colors.purple,
child: const Text('No spacing'),
),
],
);
}
}
// EdgeInsets methods:
// 1. EdgeInsets.all(double value)
// 2. EdgeInsets.only({left, right, top, bottom})
// 3. EdgeInsets.fromLTRB(left, top, right, bottom)
// 4. EdgeInsets.symmetric({horizontal, vertical})
// 5. EdgeInsets.zero
What's happening here? - all() applies same value to all sides - only() sets specific sides - fromLTRB() sets all sides individually - symmetric() sets horizontal/vertical - zero creates no spacing
BoxDecoration
BoxDecoration adds visual styling.
// BoxDecoration properties
class BoxDecorationExample extends StatelessWidget {
const BoxDecorationExample({super.key});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
// 1. Color
color: Colors.blue,
// 2. Border
border: Border.all(
color: Colors.black,
width: 2,
style: BorderStyle.solid,
),
// 3. BorderRadius
borderRadius: BorderRadius.circular(10),
// or individual corners:
// borderRadius: BorderRadius.only(
// topLeft: Radius.circular(10),
// topRight: Radius.circular(10),
// ),
// 4. BoxShadow
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.3),
offset: const Offset(5, 5),
blurRadius: 10,
spreadRadius: 2,
),
],
// 5. Gradient
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.blue, Colors.purple],
),
// 6. Shape
shape: BoxShape.rectangle,
// or BoxShape.circle
// 7. Image
// image: DecorationImage(
// image: AssetImage('assets/image.png'),
// fit: BoxFit.cover,
// ),
),
child: const Padding(
padding: EdgeInsets.all(20),
child: Text(
'Styled Container',
style: TextStyle(color: Colors.white),
),
),
);
}
}
// Decoration types:
// 1. BoxDecoration - Most common
// 2. ShapeDecoration - For shapes
// 3. FlutterLogoDecoration - For logo
// 4. CustomPainter - Custom decoration
What's happening here? - Color fills the background - Border surrounds the container - BorderRadius rounds corners - BoxShadow adds shadow effects - Gradient creates color transitions - Shape changes box shape
Spacing Widgets
Widgets for spacing in Flutter layouts.
// 1. Padding - Adds internal space
class PaddingExample extends StatelessWidget {
const PaddingExample({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(20),
child: Container(
color: Colors.blue,
child: const Text('Padded content'),
),
);
}
}
// 2. Margin - Using Container
class MarginExample extends StatelessWidget {
const MarginExample({super.key});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(20),
color: Colors.blue,
child: const Text('Content with margin'),
);
}
}
// 3. SizedBox - Fixed spacing
class SizedBoxExample extends StatelessWidget {
const SizedBoxExample({super.key});
@override
Widget build(BuildContext context) {
return Column(
children: [
const Text('Top text'),
// Fixed space
const SizedBox(height: 20),
const Text('Middle text'),
// Fixed space
const SizedBox(width: 50, height: 50),
const Text('Bottom text'),
],
);
}
}
// 4. Spacer - Flexible spacing
class SpacerExample extends StatelessWidget {
const SpacerExample({super.key});
@override
Widget build(BuildContext context) {
return Row(
children: [
const Text('Left'),
// Takes available space
const Spacer(),
const Text('Center'),
const Spacer(),
const Text('Right'),
],
);
}
}
// 5. Expanded - Flexible with constraints
class ExpandedExample extends StatelessWidget {
const ExpandedExample({super.key});
@override
Widget build(BuildContext context) {
return Row(
children: [
Container(
width: 50,
color: Colors.red,
child: const Text('Fixed'),
),
Expanded(
child: Container(
color: Colors.blue,
child: const Text('Flexible'),
),
),
],
);
}
}
What's happening here? - Padding adds internal spacing - Container margin adds external spacing - SizedBox adds fixed spacing - Spacer adds flexible spacing - Expanded takes available space
Customizing Box Model
Custom box model for specific needs.
// Custom box model example
class CustomBoxModel extends StatelessWidget {
const CustomBoxModel({super.key});
@override
Widget build(BuildContext context) {
return Container(
// Custom margin
margin: const EdgeInsets.only(
left: 20,
right: 20,
top: 10,
bottom: 10,
),
// Custom padding
padding: const EdgeInsets.only(
left: 15,
right: 15,
top: 8,
bottom: 8,
),
// Custom decoration
decoration: BoxDecoration(
color: Colors.blue,
border: Border(
left: BorderSide(
color: Colors.red,
width: 5,
),
right: BorderSide(
color: Colors.green,
width: 5,
),
top: BorderSide(
color: Colors.yellow,
width: 5,
),
bottom: BorderSide(
color: Colors.purple,
width: 5,
),
),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(10),
bottomRight: Radius.circular(10),
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 10,
spreadRadius: 2,
),
],
),
child: const Text(
'Custom Box',
style: TextStyle(
color: Colors.white,
fontSize: 18,
),
),
);
}
}
// Box model calculations:
// Total width = margin.left + border.left + padding.left + content.width + padding.right + border.right + margin.right
// Total height = margin.top + border.top + padding.top + content.height + padding.bottom + border.bottom + margin.bottom
What's happening here? - Custom margins and padding - Individual border sides - Specific border radius - Shadow effects - Full box model control
Common Layout Patterns
Common patterns using the box model.
// 1. Card with elevation
class CardPattern extends StatelessWidget {
const CardPattern({super.key});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 5),
),
],
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Card Title',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
const Text(
'Card content goes here. This is a typical card pattern.',
),
],
),
),
);
}
}
// 2. Button with padding
class ButtonPattern extends StatelessWidget {
const ButtonPattern({super.key});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(8),
),
child: const Padding(
padding: EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
child: Text(
'Click Me',
style: TextStyle(
color: Colors.white,
fontSize: 16,
),
),
),
);
}
}
// 3. Section with spacing
class SectionPattern extends StatelessWidget {
const SectionPattern({super.key});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.all(16),
child: Text(
'Section Title',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
),
const Divider(height: 1),
Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Container(
margin: const EdgeInsets.only(bottom: 10),
padding: const EdgeInsets.all(12),
color: Colors.grey[200],
child: const Text('Item 1'),
),
Container(
margin: const EdgeInsets.only(bottom: 10),
padding: const EdgeInsets.all(12),
color: Colors.grey[200],
child: const Text('Item 2'),
),
Container(
padding: const EdgeInsets.all(12),
color: Colors.grey[200],
child: const Text('Item 3'),
),
],
),
),
],
);
}
}
What's happening here? - Card with shadow and padding - Button with internal spacing - Section with consistent spacing - Common UI patterns - Reusable designs
Best Practices
Use Consistent Spacing
// Good - Consistent spacing
class ConsistentSpacing extends StatelessWidget {
const ConsistentSpacing({super.key});
static const double spacing = 8;
@override
Widget build(BuildContext context) {
return Column(
children: [
const SizedBox(height: spacing),
Container(
padding: const EdgeInsets.all(spacing),
child: const Text('Item 1'),
),
const SizedBox(height: spacing),
Container(
padding: const EdgeInsets.all(spacing),
child: const Text('Item 2'),
),
],
);
}
}
// Bad - Inconsistent spacing
class InconsistentSpacing extends StatelessWidget {
const InconsistentSpacing({super.key});
@override
Widget build(BuildContext context) {
return Column(
children: [
const SizedBox(height: 5),
Container(
padding: const EdgeInsets.all(10),
child: const Text('Item 1'),
),
const SizedBox(height: 15),
Container(
padding: const EdgeInsets.all(8),
child: const Text('Item 2'),
),
],
);
}
}
Use EdgeInsets Constants
// Good - Using constants
class SpacingConstants {
static const EdgeInsets cardPadding = EdgeInsets.all(16);
static const EdgeInsets buttonPadding = EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
);
}
// Bad - Hardcoding values everywhere
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16), // Repeated
child: Container(
padding: const EdgeInsets.all(12), // Different
child: const Text('Content'),
),
);
}
Avoid Overuse of Container
// Good - Using specific widgets
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16),
child: const Text('Content'),
);
}
// Bad - Using Container for everything
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16),
child: const Text('Content'),
);
}
Common Mistakes
Incorrect Margin Usage
Wrong:
// Margin on child to separate from parent
Container(
child: Container(
margin: const EdgeInsets.all(20), // Better on parent
color: Colors.blue,
child: const Text('Content'),
),
)
Correct:
// Margin on parent or using Padding
Padding(
padding: const EdgeInsets.all(20),
child: Container(
color: Colors.blue,
child: const Text('Content'),
),
)
Not Considering Box Model
Wrong:
// Text might overflow due to padding
Container(
padding: const EdgeInsets.all(20),
width: 100,
child: const Text(
'Very long text that might overflow',
),
)
Correct:
// Account for padding in constraints
Container(
padding: const EdgeInsets.all(20),
width: 100, // Width includes padding
child: const Text(
'Long text',
overflow: TextOverflow.ellipsis,
),
)
Summary
The box model controls spacing around and within widgets through margin, border, padding, and content. Understanding these components helps create consistent, well-spaced layouts. Use EdgeInsets for spacing, BoxDecoration for styling, and follow best practices for maintainable code.
Next Steps
Did You Know?
- Container implements the full box model
- EdgeInsets provides 5 different constructors
- Margin and padding are both EdgeInsets
- BoxDecoration supports gradient and shadows
- The box model affects layout calculations
- CSS and Flutter box models are similar
- Padding adds space inside, margin adds space outside