Container, Padding & SizedBox
Understanding Container
The Container widget is one of the most versatile widgets in Flutter. It combines common painting, positioning, and sizing widgets into a single convenient widget. However, understanding when and how Container uses different render objects under the hood is key to writing efficient layouts.
When you set different properties on Container, Flutter actually composes multiple widgets internally. For example, setting color creates a ColoredBox, setting padding wraps the child in a Padding widget, setting alignment wraps it in an Align, and setting decoration uses a DecoratedBox.
Container Deep Dive
// Container with multiple properties
// Internally composes: Align > Padding > DecoratedBox > ConstrainedBox
Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(16.0),
margin: const EdgeInsets.symmetric(horizontal: 20.0),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(12.0),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8.0,
offset: const Offset(0, 2),
),
],
),
constraints: const BoxConstraints(
minHeight: 100.0,
maxWidth: 400.0,
),
child: const Text('Hello Flutter!'),
)
color and decoration on a Container. If you need a background color with decoration, include the color inside the BoxDecoration using its color property.
Container Without a Child
When a Container has no child, it tries to be as big as possible, filling all available space from its parent. This is useful for creating colored backgrounds or decorative blocks.
Container Without Child
// This Container fills all available space
Scaffold(
body: Container(
color: Colors.blue,
// No child - fills the entire Scaffold body
),
)
// This Container has a fixed size
Container(
width: 200.0,
height: 100.0,
color: Colors.red,
// No child but constrained to 200x100
)
The Padding Widget
While Container has a padding property, Flutter also provides a dedicated Padding widget. When all you need is padding around a child, using the Padding widget is more efficient and expressive than wrapping in a Container.
Padding Widget vs Container Padding
// Using Padding widget (preferred when only padding is needed)
const Padding(
padding: EdgeInsets.all(16.0),
child: Text('Clean and efficient'),
)
// Using Container just for padding (wasteful)
Container(
padding: const EdgeInsets.all(16.0),
child: const Text('Works but unnecessary overhead'),
)
// Different padding constructors
const Padding(
padding: EdgeInsets.symmetric(
horizontal: 24.0,
vertical: 12.0,
),
child: Text('Symmetric padding'),
)
const Padding(
padding: EdgeInsets.only(
left: 16.0,
top: 8.0,
right: 16.0,
bottom: 24.0,
),
child: Text('Directional padding'),
)
// For RTL-safe directional padding
const Padding(
padding: EdgeInsetsDirectional.only(
start: 16.0,
end: 8.0,
),
child: Text('RTL-aware padding'),
)
EdgeInsetsDirectional instead of EdgeInsets when your app supports both LTR and RTL languages. It uses start and end instead of left and right, automatically flipping for RTL layouts.
SizedBox Fundamentals
The SizedBox widget is a box with a specified size. It forces its child to have a specific width and/or height. When used without a child, it acts as an invisible spacer — one of the most common spacing patterns in Flutter.
SizedBox Basics
// Fixed-size box with a child
const SizedBox(
width: 200.0,
height: 100.0,
child: Card(
child: Center(child: Text('Fixed size card')),
),
)
// SizedBox as a spacer (extremely common pattern)
Column(
children: const [
Text('First item'),
SizedBox(height: 16.0), // Vertical spacing
Text('Second item'),
SizedBox(height: 24.0), // Larger vertical spacing
Text('Third item'),
],
)
Row(
children: const [
Icon(Icons.star),
SizedBox(width: 8.0), // Horizontal spacing
Text('Star rating'),
],
)
SizedBox.expand and SizedBox.fromSize
Flutter provides convenient named constructors for common SizedBox patterns:
SizedBox Named Constructors
// SizedBox.expand - fills all available space
// Equivalent to SizedBox(width: double.infinity, height: double.infinity)
const SizedBox.expand(
child: ColoredBox(
color: Colors.green,
child: Center(child: Text('I fill everything!')),
),
)
// SizedBox.shrink - takes minimum space (0x0)
// Useful as a placeholder or empty widget
const SizedBox.shrink() // width: 0, height: 0
// SizedBox.fromSize - create from a Size object
SizedBox.fromSize(
size: const Size(150.0, 75.0),
child: const Placeholder(),
)
// SizedBox with double.infinity for one axis
const SizedBox(
width: double.infinity, // Full width
height: 50.0, // Fixed height
child: ColoredBox(
color: Colors.blue,
child: Center(child: Text('Full-width bar')),
),
)
ConstrainedBox
While SizedBox sets an exact size, ConstrainedBox lets you define minimum and maximum constraints for width and height. This gives you more flexible sizing control.
ConstrainedBox Examples
// Set minimum dimensions
ConstrainedBox(
constraints: const BoxConstraints(
minWidth: 100.0,
minHeight: 50.0,
),
child: const Text('At least 100x50'),
)
// Set maximum dimensions
ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 300.0,
maxHeight: 200.0,
),
child: const Text('No larger than 300x200'),
)
// Combine min and max
ConstrainedBox(
constraints: const BoxConstraints(
minWidth: 100.0,
maxWidth: 300.0,
minHeight: 50.0,
maxHeight: 150.0,
),
child: Container(
color: Colors.amber,
child: const Text('Flexible within bounds'),
),
)
// Named constructors
ConstrainedBox(
constraints: BoxConstraints.tight(const Size(200, 100)),
child: const Placeholder(), // Exactly 200x100
)
ConstrainedBox(
constraints: BoxConstraints.loose(const Size(300, 200)),
child: const Placeholder(), // Up to 300x200
)
FractionallySizedBox
The FractionallySizedBox widget sizes its child to a fraction of the available space. This is incredibly useful for responsive layouts where you want elements to take a percentage of their parent’s size.
FractionallySizedBox Examples
// Take 80% of parent width and 50% of parent height
FractionallySizedBox(
widthFactor: 0.8,
heightFactor: 0.5,
child: Container(
color: Colors.purple.shade100,
child: const Center(
child: Text('80% width, 50% height'),
),
),
)
// Responsive container - 90% width on small screens
SizedBox(
width: double.infinity,
child: FractionallySizedBox(
widthFactor: 0.9,
child: Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: const [
Text('Responsive Card'),
SizedBox(height: 8.0),
Text('Takes 90% of available width'),
],
),
),
),
),
)
// FractionallySizedBox with alignment
FractionallySizedBox(
alignment: Alignment.topLeft,
widthFactor: 0.6,
heightFactor: 0.4,
child: Container(
color: Colors.teal,
child: const Center(child: Text('Top-left 60%x40%')),
),
)
Practical Examples
Spacing Pattern: Consistent Gaps
Use SizedBox to create consistent spacing throughout your app. Many teams define spacing constants for uniformity:
Spacing Constants Pattern
// Define spacing constants
abstract class AppSpacing {
static const double xs = 4.0;
static const double sm = 8.0;
static const double md = 16.0;
static const double lg = 24.0;
static const double xl = 32.0;
static const double xxl = 48.0;
// Reusable SizedBox spacers
static const SizedBox verticalXs = SizedBox(height: xs);
static const SizedBox verticalSm = SizedBox(height: sm);
static const SizedBox verticalMd = SizedBox(height: md);
static const SizedBox verticalLg = SizedBox(height: lg);
static const SizedBox horizontalSm = SizedBox(width: sm);
static const SizedBox horizontalMd = SizedBox(width: md);
}
// Usage in a form
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text('Create Account', style: TextStyle(fontSize: 24)),
AppSpacing.verticalLg,
const TextField(decoration: InputDecoration(labelText: 'Name')),
AppSpacing.verticalMd,
const TextField(decoration: InputDecoration(labelText: 'Email')),
AppSpacing.verticalMd,
const TextField(decoration: InputDecoration(labelText: 'Password')),
AppSpacing.verticalLg,
ElevatedButton(
onPressed: () {},
child: const Text('Sign Up'),
),
],
)
const static fields means Flutter reuses the same widget instance everywhere, saving memory and improving performance.
Responsive Container Pattern
Responsive Content Container
class ResponsiveContainer extends StatelessWidget {
final Widget child;
final double maxWidth;
const ResponsiveContainer({
super.key,
required this.child,
this.maxWidth = 600.0,
});
@override
Widget build(BuildContext context) {
return Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: maxWidth),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: child,
),
),
);
}
}
// Usage - content never exceeds 600px but fills smaller screens
Scaffold(
body: ResponsiveContainer(
child: Column(
children: const [
Text('This content is readable on all screen sizes'),
SizedBox(height: 16),
Text('Max width is 600px, centered on large screens'),
],
),
),
)
Container when you need multiple properties (decoration, alignment, padding) together. Use Padding when you only need padding. Use SizedBox for fixed sizes and spacing. Use ConstrainedBox for min/max constraints. Use FractionallySizedBox for percentage-based sizing.