Flutter uses a constraint-based layout system. Understanding how constraints flow down and sizes flow up is key.
Padding and margin
// Padding widget
Padding(
padding: EdgeInsets.all(16), // all sides
child: Text('Hello'),
)
Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text('Hello'),
)
Padding(
padding: EdgeInsets.only(left: 8, top: 4),
child: Text('Hello'),
)
// Margin via Container
Container(
margin: EdgeInsets.all(8),
child: Text('Hello'),
)Alignment and Centering
// Center a child
Center(child: Text('Centered'))
// Align within a specific position
Align(
alignment: Alignment.topLeft,
child: Text('Top Left'),
)
Align(
alignment: Alignment.bottomRight,
child: Text('Bottom Right'),
)Flex, Expanded, and Spacer
// Expanded — fill available space
Row(
children: [
Expanded(flex: 1, child: Container(color: Colors.red)),
Expanded(flex: 2, child: Container(color: Colors.blue)), // twice as wide
],
)
// Flexible — can shrink or expand
Row(
children: [
Flexible(child: Text('This text can shrink')),
Expanded(child: Container(color: Colors.grey)),
],
)
// Spacer — empty space in flex layouts
Row(
children: [
Text('Left'),
Spacer(),
Text('Right'),
],
)SizedBox and ConstrainedBox
// Fixed size
SizedBox(width: 100, height: 50, child: Container(color: Colors.blue))
// Just spacing (no child)
SizedBox(height: 16) // vertical gap
SizedBox(width: 8) // horizontal gap
// Constrain child
ConstrainedBox(
constraints: BoxConstraints(
minWidth: 100,
maxWidth: 300,
minHeight: 50,
),
child: Text('Constrained'),
)Theme and styling
// Define a theme for the whole app
MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
textTheme: TextTheme(
headlineLarge: TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
bodyLarge: TextStyle(fontSize: 16),
labelSmall: TextStyle(fontSize: 11, letterSpacing: 0.5),
),
cardTheme: CardTheme(
elevation: 2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
),
),
home: HomeScreen(),
);Access theme in widgets
// Read theme values
final theme = Theme.of(context);
Text(
'Styled text',
style: theme.textTheme.headlineLarge?.copyWith(color: theme.colorScheme.primary),
);
// Use Material 3 color roles
Container(color: theme.colorScheme.surface);
Container(color: theme.colorScheme.surfaceVariant);
Text('Error', style: TextStyle(color: theme.colorScheme.error));
ElevatedButton(style: ElevatedButton.styleFrom(
backgroundColor: theme.colorScheme.primary,
foregroundColor: theme.colorScheme.onPrimary,
));Responsive design
MediaQuery — device info
final size = MediaQuery.of(context).size;
final width = size.width;
final height = size.height;
final padding = MediaQuery.of(context).padding; // safe area insets
final orientation = MediaQuery.of(context).orientation; // portrait / landscapeLayoutBuilder — responsive layouts
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 600) {
return Row(children: [sidebar, Expanded(child: content)]);
} else {
return Column(children: [content]);
}
},
)Breakpoint pattern
enum ScreenType { mobile, tablet, desktop }
ScreenType getScreenType(double width) {
if (width > 1200) return ScreenType.desktop;
if (width > 600) return ScreenType.tablet;
return ScreenType.mobile;
}SafeArea and system UI
// Avoid notches and system overlays
SafeArea(
child: Column(children: [...]),
)
// SliverAppBar for scrollable app bars
CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 200,
floating: false,
pinned: true,
flexibleSpace: FlexibleSpaceBar(title: Text('Scroll')),
),
SliverList(delegate: SliverChildBuilderDelegate(
(context, index) => ListTile(title: Text('Item $index')),
childCount: 100,
)),
],
)Animations basics
// Implicit animations — animate property changes
AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
width: isExpanded ? 200 : 100,
height: isExpanded ? 200 : 100,
color: isExpanded ? Colors.blue : Colors.grey,
)
AnimatedOpacity(opacity: isVisible ? 1.0 : 0.0, duration: Duration(milliseconds: 300))
AnimatedPositioned(duration: Duration(milliseconds: 300), left: offset, top: offset, child: icon)
AnimatedSwitcher(duration: Duration(milliseconds: 300), child: isVisible ? Text('Yes') : Text('No'))