Flutter Widgets Guide
A comprehensive guide to Flutter widgets with detailed explanations, use cases, and practical examples. This guide helps you understand when, why, and how to use each widget effectively.
Table of Contents
- Widget Fundamentals
- Layout Widgets
- Text & Typography
- Interactive Widgets
- Lists & Grids
- Material Design Widgets
- Performance Considerations
Widget Fundamentals
Understanding Widgets
What are Widgets? Widgets are the building blocks of Flutter UI. Everything you see on screen is a widget - from text and buttons to complex layouts. Widgets describe what their view should look like given their current configuration and state.
Key Concepts:
- Immutable: Widgets are immutable - once created, they cannot be changed
- Composition: Complex UIs are built by composing simple widgets
- Rebuild: When state changes, widgets rebuild to reflect new state
Widget Types
StatelessWidget
When to use: When your widget doesn't need to maintain any state or change over time.
Use cases:
- Display static content (text, images, icons)
- Show data that doesn't change
- Simple UI components that don't respond to user input
// Stateless Widget - immutable, no internal state
class WelcomeMessage extends StatelessWidget {
final String userName;
const WelcomeMessage({Key? key, required this.userName}) : super(key: key);
Widget build(BuildContext context) {
return Text('Welcome, $userName!');
}
}
// Usage
WelcomeMessage(userName: 'John')
StatefulWidget
When to use: When your widget needs to maintain state that can change over time.
Use cases:
- Forms with user input
- Animations
- Widgets that respond to user interactions
- Data that changes based on external factors
// Stateful Widget - can change over time
class CounterWidget extends StatefulWidget {
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _count = 0;
void _increment() {
setState(() {
_count++;
});
}
Widget build(BuildContext context) {
return Column(
children: [
Text('Count: $_count'),
ElevatedButton(
onPressed: _increment,
child: Text('Increment'),
),
],
);
}
}
Widget Tree Structure
Understanding the Widget Tree: Flutter builds your UI as a tree of widgets. Each widget can have children, creating a hierarchical structure.
MaterialApp( // Root widget
home: Scaffold( // Provides basic app structure
appBar: AppBar( // Top app bar
title: Text('My App'),
),
body: Column( // Main content area
children: [
Text('Hello'), // Child widget
Container( // Another child widget
child: Text('World'),
),
],
),
),
)
Why this matters:
- Performance: Flutter only rebuilds widgets that actually changed
- Layout: Parent widgets control how children are positioned and sized
- Inheritance: Child widgets can access data from parent widgets through context
Layout Widgets
Basic Layout Widgets
Column
What it does: Arranges its children in a vertical array.
When to use:
- Stacking widgets vertically (top to bottom)
- Creating forms with multiple input fields
- Building page layouts with header, content, and footer
Key properties:
mainAxisAlignment: Controls vertical alignmentcrossAxisAlignment: Controls horizontal alignmentmainAxisSize: Controls how much vertical space to take
// Basic Column
Column(
children: [
Text('Header'),
Text('Content'),
Text('Footer'),
],
)
// Column with alignment
Column(
mainAxisAlignment: MainAxisAlignment.center, // Center vertically
crossAxisAlignment: CrossAxisAlignment.start, // Align left horizontally
children: [
Text('Item 1'),
Text('Item 2'),
Text('Item 3'),
],
)
// Column that takes full height
Column(
mainAxisSize: MainAxisSize.max, // Take all available height
children: [
Expanded(child: Text('Takes remaining space')),
Text('Fixed height'),
],
)
Row
What it does: Arranges its children in a horizontal array.
When to use:
- Placing widgets side by side
- Creating navigation bars
- Building form layouts with labels and inputs
Key properties:
mainAxisAlignment: Controls horizontal alignmentcrossAxisAlignment: Controls vertical alignmentmainAxisSize: Controls how much horizontal space to take
// Basic Row
Row(
children: [
Icon(Icons.star),
Text('Rating: 4.5'),
],
)
// Row with space distribution
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, // Space between items
children: [
Text('Left'),
Text('Center'),
Text('Right'),
],
)
// Row with flexible children
Row(
children: [
Text('Label: '),
Expanded(
child: TextField(decoration: InputDecoration(hintText: 'Enter value')),
),
],
)
Stack
What it does: Overlays multiple widgets on top of each other.
When to use:
- Creating overlays (like badges on icons)
- Building complex layouts with background and foreground elements
- Implementing floating action buttons
- Creating image overlays with text
Key properties:
alignment: Controls how positioned children are alignedfit: Controls how non-positioned children are sized
// Basic Stack
Stack(
children: [
Container(
width: 200,
height: 200,
color: Colors.blue,
),
Positioned(
top: 10,
left: 10,
child: Text('Overlay text'),
),
],
)
// Stack with centered overlay
Stack(
alignment: Alignment.center, // Center non-positioned children
children: [
Image.network('https://example.com/image.jpg'),
Container(
padding: EdgeInsets.all(8),
color: Colors.black54,
child: Text(
'Overlay',
style: TextStyle(color: Colors.white),
),
),
],
)
// Badge on icon
Stack(
children: [
Icon(Icons.notifications, size: 30),
Positioned(
right: 0,
top: 0,
child: Container(
padding: EdgeInsets.all(2),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(10),
),
constraints: BoxConstraints(
minWidth: 16,
minHeight: 16,
),
child: Text(
'3',
style: TextStyle(
color: Colors.white,
fontSize: 10,
),
textAlign: TextAlign.center,
),
),
),
],
)
Advanced Layout Widgets
Expanded
What it does: Forces a child of a Row, Column, or Flex to expand to fill the available space.
When to use:
- Making a widget take up remaining space in a Row/Column
- Creating flexible layouts where some elements are fixed size and others expand
- Building responsive layouts
Key properties:
flex: Controls how much space to take relative to other Expanded widgets
// Basic Expanded
Row(
children: [
Text('Fixed width text'),
Expanded(
child: Text('This text will take all remaining horizontal space'),
),
],
)
// Multiple Expanded widgets
Row(
children: [
Expanded(
flex: 1, // Takes 1/3 of space
child: Container(color: Colors.red),
),
Expanded(
flex: 2, // Takes 2/3 of space
child: Container(color: Colors.blue),
),
],
)
// Form layout with label and input
Row(
children: [
Text('Email: '),
Expanded(
child: TextField(
decoration: InputDecoration(
hintText: 'Enter your email',
border: OutlineInputBorder(),
),
),
),
],
)
Flexible
What it does: Similar to Expanded but allows the child to be smaller than the available space.
When to use:
- When you want a widget to be flexible but not necessarily fill all space
- Creating layouts where widgets can shrink if needed
- Building responsive designs
// Flexible vs Expanded
Row(
children: [
Text('Fixed'),
Flexible(
child: Text('Can shrink if needed'),
),
Expanded(
child: Text('Must take remaining space'),
),
],
)
// Flexible with flex factor
Row(
children: [
Flexible(
flex: 1,
child: Container(
height: 50,
color: Colors.red,
child: Text('Can shrink'),
),
),
Flexible(
flex: 2,
child: Container(
height: 50,
color: Colors.blue,
child: Text('Takes more space when available'),
),
),
],
)
Wrap
What it does: Automatically wraps its children to the next line when they don't fit.
When to use:
- Creating tag clouds or chip lists
- Building responsive grids
- Displaying dynamic content that might overflow
Key properties:
spacing: Horizontal space between childrenrunSpacing: Vertical space between linesalignment: How children are aligned within each line
// Basic Wrap
Wrap(
children: [
Chip(label: Text('Flutter')),
Chip(label: Text('Dart')),
Chip(label: Text('Mobile Development')),
Chip(label: Text('Cross-platform')),
Chip(label: Text('UI Framework')),
],
)
// Wrap with spacing
Wrap(
spacing: 8.0, // Horizontal space between children
runSpacing: 4.0, // Vertical space between lines
alignment: WrapAlignment.center, // Center children in each line
children: [
ActionChip(
label: Text('Action 1'),
onPressed: () {},
),
ActionChip(
label: Text('Action 2'),
onPressed: () {},
),
ActionChip(
label: Text('Action 3'),
onPressed: () {},
),
],
)
Container vs SizedBox
Container vs SizedBox - Understanding the Difference:
SizedBox
What it does: A widget with a specified size that forces its child to be exactly that size.
When to use:
- When you need a widget with exact dimensions
- Creating spacing between widgets
- When you don't need decoration or padding
- Performance-critical scenarios (lighter than Container)
// SizedBox with child
SizedBox(
width: 100,
height: 100,
child: Text('Fixed size text'),
)
// SizedBox for spacing
Column(
children: [
Text('First item'),
SizedBox(height: 20), // 20px vertical spacing
Text('Second item'),
],
)
// SizedBox as spacer
Row(
children: [
Text('Left'),
SizedBox(width: 50), // 50px horizontal spacing
Text('Right'),
],
)
Container
What it does: A convenience widget that combines common painting, positioning, and sizing widgets.
When to use:
- When you need decoration (colors, borders, shadows)
- When you need padding or margin
- When you need complex styling
- When you need to constrain a child's size with additional styling
// Container with decoration
Container(
width: 100,
height: 100,
margin: EdgeInsets.all(8.0), // Space outside the container
padding: EdgeInsets.all(16.0), // Space inside the container
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(8.0),
boxShadow: [
BoxShadow(
color: Colors.grey,
blurRadius: 4.0,
offset: Offset(0, 2),
),
],
),
child: Text('Styled container'),
)
// Container with just padding
Container(
padding: EdgeInsets.all(16.0),
child: Text('Padded text'),
)
// Container with constraints
Container(
constraints: BoxConstraints(
minWidth: 100,
maxWidth: 200,
minHeight: 50,
maxHeight: 100,
),
child: Text('Constrained text'),
)
Key Differences:
- SizedBox: Lightweight, exact sizing, no decoration
- Container: Feature-rich, can have decoration, padding, margin, constraints
Performance Tip: Use SizedBox when you only need sizing, use Container when you need styling.
Padding & Margin
Padding
What it does: Adds space inside a widget (between the widget's border and its content).
When to use:
- Adding space around text or content
- Creating breathing room within widgets
- Consistent spacing in layouts
// Basic padding
Padding(
padding: EdgeInsets.all(16.0),
child: Text('Text with padding'),
)
// Different padding for each side
Padding(
padding: EdgeInsets.only(
left: 16.0,
right: 16.0,
top: 8.0,
bottom: 8.0,
),
child: Text('Custom padding'),
)
// Symmetric padding
Padding(
padding: EdgeInsets.symmetric(
horizontal: 16.0, // Left and right
vertical: 8.0, // Top and bottom
),
child: Text('Symmetric padding'),
)
Margin (via Container)
What it does: Adds space outside a widget (between the widget and its neighbors).
When to use:
- Creating space between widgets
- Building layouts with gaps
- Separating sections of your UI
// Container with margin
Container(
margin: EdgeInsets.all(8.0),
child: Text('Text with margin'),
)
// Margin between widgets
Column(
children: [
Container(
margin: EdgeInsets.only(bottom: 16.0),
child: Text('First widget'),
),
Text('Second widget'),
],
)
Text & Typography
Text Widgets
Text
What it does: Displays a string of text with single styling.
When to use:
- Displaying simple text content
- Labels and titles
- When all text has the same style
// Basic text
Text('Hello World')
// Text with style
Text(
'Styled Text',
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold,
color: Colors.blue,
fontFamily: 'Roboto',
letterSpacing: 1.2,
height: 1.5, // Line height multiplier
),
)
// Text with alignment
Text(
'Centered text',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18),
)
// Text with overflow handling
Text(
'This is a very long text that might overflow the available space',
overflow: TextOverflow.ellipsis, // Shows "..." when text overflows
maxLines: 2, // Limit to 2 lines
)
RichText
What it does: Displays text with multiple styles within the same text widget.
When to use:
- When you need different styles within the same text
- Highlighting specific words or phrases
- Creating complex text layouts
- Links within text
// RichText with multiple styles
RichText(
text: TextSpan(
style: TextStyle(color: Colors.black, fontSize: 16), // Default style
children: [
TextSpan(text: 'Hello '),
TextSpan(
text: 'World',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blue,
fontSize: 20,
),
),
TextSpan(text: '!'),
],
),
)
// RichText with clickable spans
RichText(
text: TextSpan(
style: TextStyle(color: Colors.black),
children: [
TextSpan(text: 'Click '),
TextSpan(
text: 'here',
style: TextStyle(
color: Colors.blue,
decoration: TextDecoration.underline,
),
recognizer: TapGestureRecognizer()
..onTap = () => print('Link tapped'),
),
TextSpan(text: ' to continue'),
],
),
)
Text Input Widgets
TextField
What it does: Creates a material design text input field.
When to use:
- Single-line text input
- Search fields
- Username/password fields
- Any form input that doesn't need validation
// Basic TextField
TextField(
decoration: InputDecoration(
labelText: 'Enter your name',
hintText: 'John Doe',
border: OutlineInputBorder(),
),
onChanged: (value) {
print('Text changed: $value');
},
)
// TextField with prefix icon
TextField(
decoration: InputDecoration(
labelText: 'Email',
hintText: 'example@email.com',
prefixIcon: Icon(Icons.email),
border: OutlineInputBorder(),
),
keyboardType: TextInputType.emailAddress,
)
// TextField with suffix and validation
TextField(
decoration: InputDecoration(
labelText: 'Password',
suffixIcon: Icon(Icons.visibility),
border: OutlineInputBorder(),
errorText: _passwordError, // Show error if not null
),
obscureText: true, // Hide text (for passwords)
onChanged: (value) {
setState(() {
_passwordError = value.length < 6 ? 'Password too short' : null;
});
},
)
TextFormField
What it does: A TextField that integrates with Form validation.
When to use:
- Forms with validation
- When you need to validate input before submission
- Complex forms with multiple fields
// TextFormField with validation
TextFormField(
decoration: InputDecoration(
labelText: 'Email',
hintText: 'Enter your email',
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter an email';
}
if (!value.contains('@')) {
return 'Please enter a valid email';
}
return null;
},
onSaved: (value) {
_email = value;
},
)
// Using with Form widget
Form(
key: _formKey,
child: Column(
children: [
TextFormField(
decoration: InputDecoration(labelText: 'Name'),
validator: (value) => value?.isEmpty ?? true ? 'Required' : null,
),
TextFormField(
decoration: InputDecoration(labelText: 'Email'),
validator: (value) => value?.isEmpty ?? true ? 'Required' : null,
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
// Process form data
}
},
child: Text('Submit'),
),
],
),
)
Interactive Widgets
Button Widgets
ElevatedButton
What it does: A material design button that lifts when pressed.
When to use:
- Primary actions in your app
- Call-to-action buttons
- When you want the button to stand out
// Basic ElevatedButton
ElevatedButton(
onPressed: () {
print('Button pressed');
},
child: Text('Press Me'),
)
// ElevatedButton with icon
ElevatedButton.icon(
onPressed: () {},
icon: Icon(Icons.save),
label: Text('Save'),
)
// ElevatedButton with custom style
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
primary: Colors.blue,
onPrimary: Colors.white,
padding: EdgeInsets.symmetric(horizontal: 32, vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text('Custom Button'),
)
// Disabled button
ElevatedButton(
onPressed: _isLoading ? null : () {}, // null disables the button
child: _isLoading
? CircularProgressIndicator(color: Colors.white)
: Text('Submit'),
)
TextButton
What it does: A flat button with no elevation.
When to use:
- Secondary actions
- Navigation buttons
- When you want a subtle button
- In dialogs and bottom sheets
// Basic TextButton
TextButton(
onPressed: () {},
child: Text('Cancel'),
)
// TextButton with custom style
TextButton(
onPressed: () {},
style: TextButton.styleFrom(
primary: Colors.red,
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
child: Text('Delete'),
)
IconButton
What it does: A button that displays an icon.
When to use:
- Toolbar actions
- Floating action buttons
- Compact actions
- When space is limited
// Basic IconButton
IconButton(
onPressed: () {},
icon: Icon(Icons.favorite),
)
// IconButton with tooltip
IconButton(
onPressed: () {},
icon: Icon(Icons.help),
tooltip: 'Help',
)
// IconButton with custom size
IconButton(
onPressed: () {},
icon: Icon(Icons.menu),
iconSize: 30,
color: Colors.blue,
)
Gesture Detection
GestureDetector
What it does: Detects gestures on its child widget.
When to use:
- Making any widget tappable
- Custom gesture handling
- When you need fine-grained control over gestures
// Basic tap detection
GestureDetector(
onTap: () => print('Tapped'),
child: Container(
color: Colors.blue,
padding: EdgeInsets.all(16),
child: Text('Tap me', style: TextStyle(color: Colors.white)),
),
)
// Multiple gestures
GestureDetector(
onTap: () => print('Single tap'),
onDoubleTap: () => print('Double tap'),
onLongPress: () => print('Long press'),
onPanUpdate: (details) => print('Pan: ${details.delta}'),
child: Container(
color: Colors.green,
child: Text('Multi-gesture widget'),
),
)
// Drag detection
GestureDetector(
onPanUpdate: (details) {
setState(() {
_position += details.delta;
});
},
child: Container(
width: 100,
height: 100,
color: Colors.red,
transform: Matrix4.translationValues(_position.dx, _position.dy, 0),
),
)
InkWell
What it does: A rectangular area that responds to touch with a ripple effect.
When to use:
- Making widgets tappable with material design ripple
- List items that should be tappable
- When you want the material design touch feedback
// Basic InkWell
InkWell(
onTap: () => print('Tapped with ripple'),
child: Container(
padding: EdgeInsets.all(16),
child: Text('Tap with ripple effect'),
),
)
// InkWell with custom splash color
InkWell(
onTap: () {},
splashColor: Colors.blue.withOpacity(0.3),
highlightColor: Colors.blue.withOpacity(0.1),
child: Container(
padding: EdgeInsets.all(16),
child: Text('Custom ripple colors'),
),
)
// InkWell in a list
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return InkWell(
onTap: () => _onItemTap(index),
child: ListTile(
title: Text(items[index].title),
subtitle: Text(items[index].subtitle),
),
);
},
)
Lists & Grids
ListView Widgets
ListView
What it does: A scrollable list of widgets arranged linearly.
When to use:
- Displaying a list of items
- When you have a small, fixed number of items
- Simple lists that don't need optimization
// Basic ListView
ListView(
children: [
ListTile(title: Text('Item 1')),
ListTile(title: Text('Item 2')),
ListTile(title: Text('Item 3')),
],
)
// ListView with custom items
ListView(
padding: EdgeInsets.all(16),
children: [
Card(
child: ListTile(
leading: Icon(Icons.person),
title: Text('John Doe'),
subtitle: Text('Software Developer'),
trailing: Icon(Icons.arrow_forward),
onTap: () => print('Tapped John'),
),
),
Card(
child: ListTile(
leading: Icon(Icons.person),
title: Text('Jane Smith'),
subtitle: Text('Designer'),
trailing: Icon(Icons.arrow_forward),
onTap: () => print('Tapped Jane'),
),
),
],
)
ListView.builder
What it does: Creates a ListView with items built on demand.
When to use:
- Large lists (performance optimization)
- Dynamic lists where items are loaded as needed
- When you have a large number of items
// ListView.builder for large lists
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return ListTile(
title: Text(item.title),
subtitle: Text(item.subtitle),
onTap: () => _onItemTap(index),
);
},
)
// ListView.builder with different item types
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
if (item.type == 'header') {
return Container(
padding: EdgeInsets.all(16),
color: Colors.grey[200],
child: Text(
item.title,
style: TextStyle(fontWeight: FontWeight.bold),
),
);
} else {
return ListTile(
title: Text(item.title),
subtitle: Text(item.subtitle),
);
}
},
)
ListView.separated
What it does: Creates a ListView with separators between items.
When to use:
- When you want visual separation between items
- Lists that need dividers or custom separators
- When you want consistent spacing
// ListView.separated with dividers
ListView.separated(
itemCount: items.length,
separatorBuilder: (context, index) => Divider(),
itemBuilder: (context, index) {
return ListTile(title: Text(items[index]));
},
)
// ListView.separated with custom separators
ListView.separated(
itemCount: items.length,
separatorBuilder: (context, index) => SizedBox(height: 8),
itemBuilder: (context, index) {
return Card(
child: ListTile(title: Text(items[index])),
);
},
)
GridView Widgets
GridView.count
What it does: Creates a grid with a fixed number of tiles in the cross axis.
When to use:
- When you know the number of columns you want
- Simple grids with fixed layout
- When you have a small number of items
// Basic GridView.count
GridView.count(
crossAxisCount: 2, // 2 columns
children: [
Container(color: Colors.red, child: Center(child: Text('1'))),
Container(color: Colors.blue, child: Center(child: Text('2'))),
Container(color: Colors.green, child: Center(child: Text('3'))),
Container(color: Colors.yellow, child: Center(child: Text('4'))),
],
)
// GridView.count with spacing
GridView.count(
crossAxisCount: 3,
crossAxisSpacing: 8.0, // Horizontal space between items
mainAxisSpacing: 8.0, // Vertical space between items
children: List.generate(9, (index) {
return Card(
child: Center(child: Text('Item ${index + 1}')),
);
}),
)
GridView.builder
What it does: Creates a grid with items built on demand.
When to use:
- Large grids (performance optimization)
- Dynamic grids where items are loaded as needed
- When you have a large number of items
// GridView.builder for large grids
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 8.0,
mainAxisSpacing: 8.0,
),
itemCount: items.length,
itemBuilder: (context, index) {
return Card(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.star, size: 32),
SizedBox(height: 8),
Text(items[index].title),
],
),
);
},
)
// GridView.builder with different item sizes
GridView.builder(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 150, // Maximum width per item
crossAxisSpacing: 8.0,
mainAxisSpacing: 8.0,
),
itemCount: items.length,
itemBuilder: (context, index) {
return Card(
child: Center(child: Text(items[index])),
);
},
)
Material Design Widgets
Card
What it does: A material design card with elevation and rounded corners.
When to use:
- Displaying content in a contained area
- List items that need visual separation
- Information cards with multiple elements
// Basic Card
Card(
child: ListTile(
title: Text('Card Title'),
subtitle: Text('Card subtitle'),
),
)
// Card with custom content
Card(
elevation: 4,
margin: EdgeInsets.all(8),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Article Title',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text('Article description goes here...'),
SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Author: John Doe'),
Text('2024-01-15'),
],
),
],
),
),
)
AppBar
What it does: A material design app bar that displays at the top of the screen.
When to use:
- Main navigation bar for your app
- Displaying page titles and actions
- Consistent app branding
// Basic AppBar
AppBar(
title: Text('My App'),
)
// AppBar with actions
AppBar(
title: Text('My App'),
actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.more_vert),
onPressed: () {},
),
],
)
// AppBar with leading widget
AppBar(
leading: IconButton(
icon: Icon(Icons.menu),
onPressed: () {},
),
title: Text('My App'),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
)
FloatingActionButton
What it does: A circular floating button that hovers over the content.
When to use:
- Primary action for the current screen
- Quick access to common actions
- When you want the action to be prominent
// Basic FloatingActionButton
FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
)
// FloatingActionButton with label
FloatingActionButton.extended(
onPressed: () {},
icon: Icon(Icons.add),
label: Text('Add Item'),
)
// FloatingActionButton with custom style
FloatingActionButton(
onPressed: () {},
backgroundColor: Colors.red,
foregroundColor: Colors.white,
child: Icon(Icons.delete),
)
Performance Considerations
When to Use Different List Widgets
ListView vs ListView.builder:
- Use
ListViewfor small, fixed lists (< 50 items) - Use
ListView.builderfor large or dynamic lists
GridView.count vs GridView.builder:
- Use
GridView.countfor small, fixed grids - Use
GridView.builderfor large or dynamic grids
Widget Optimization Tips
- Use const constructors when possible:
// Good - const constructor
const Text('Hello')
// Avoid - non-const constructor
Text('Hello')
- Extract widgets for complex UI:
// Good - extracted widget
class UserCard extends StatelessWidget {
final User user;
const UserCard({Key? key, required this.user}) : super(key: key);
Widget build(BuildContext context) {
return Card(
child: ListTile(
title: Text(user.name),
subtitle: Text(user.email),
),
);
}
}
- Use appropriate list widgets for performance:
// For large lists, use builder pattern
ListView.builder(
itemCount: largeList.length,
itemBuilder: (context, index) => UserCard(user: largeList[index]),
)
References
- Flutter Widget Catalog (flutter.dev)
- Flutter Widgets API (api.flutter.dev)
- Flutter Layout Tutorial (flutter.dev)
- Flutter State Management (flutter.dev)
- Flutter Performance Best Practices (flutter.dev)
- Material Design Guidelines (material.io)