Using ListTile for Beautiful Lists in Flutter
•12 min read
ListTile is one of Flutter's most versatile widgets for creating beautiful and functional lists. This comprehensive guide will show you how to use ListTile effectively in your Flutter applications.
Basic ListTile Implementation
Simple ListTile Example
ListTile( leading: Icon(Icons.person), title: Text('John Doe'), subtitle: Text('Software Developer'), trailing: Icon(Icons.arrow_forward_ios), onTap: () { print('ListTile tapped'); }, )
Basic List with Multiple ListTiles
class ContactList extends StatelessWidget { final List<Contact> contacts = [ Contact('John Doe', 'Developer', Icons.work), Contact('Jane Smith', 'Designer', Icons.palette), Contact('Mike Johnson', 'Manager', Icons.business), ]; @override Widget build(BuildContext context) { return ListView.builder( itemCount: contacts.length, itemBuilder: (context, index) { final contact = contacts[index]; return ListTile( leading: Icon(contact.icon), title: Text(contact.name), subtitle: Text(contact.role), trailing: Icon(Icons.chevron_right), onTap: () { // Handle tap }, ); }, ); } } class Contact { final String name; final String role; final IconData icon; Contact(this.name, this.role, this.icon); }
Styled ListTile Variations
1. Custom Styled ListTile
class StyledListTile extends StatelessWidget { final String title; final String subtitle; final IconData leadingIcon; final VoidCallback onTap; const StyledListTile({ Key? key, required this.title, required this.subtitle, required this.leadingIcon, required this.onTap, }) : super(key: key); @override Widget build(BuildContext context) { return Card( elevation: 2, margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4), child: ListTile( leading: Container( padding: EdgeInsets.all(8), decoration: BoxDecoration( color: Theme.of(context).primaryColor.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Icon( leadingIcon, color: Theme.of(context).primaryColor, ), ), title: Text( title, style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), ), subtitle: Text( subtitle, style: TextStyle( color: Colors.grey[600], fontSize: 14, ), ), trailing: Icon( Icons.arrow_forward_ios, size: 16, color: Colors.grey[400], ), onTap: onTap, contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), ); } }
2. ListTile with Custom Layout
class ExpandedListTile extends StatelessWidget { @override Widget build(BuildContext context) { return Card( child: ListTile( leading: CircleAvatar( backgroundImage: NetworkImage('https://example.com/avatar.jpg'), ), title: Row( children: [ Text('Main Title'), SizedBox(width: 8), Container( padding: EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: Colors.green, borderRadius: BorderRadius.circular(12), ), child: Text( 'Active', style: TextStyle( color: Colors.white, fontSize: 12, ), ), ), ], ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Primary subtitle'), Text( 'Secondary information', style: TextStyle( color: Colors.grey[500], fontSize: 12, ), ), ], ), trailing: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( '12:30 PM', style: TextStyle( fontSize: 12, color: Colors.grey[600], ), ), Container( padding: EdgeInsets.all(6), decoration: BoxDecoration( color: Colors.blue, shape: BoxShape.circle, ), child: Text( '3', style: TextStyle( color: Colors.white, fontSize: 10, ), ), ), ], ), isThreeLine: true, ), ); } }
Advanced Implementations
1. Swipeable ListTile
class SwipeableListTile extends StatelessWidget { final String title; final String subtitle; final VoidCallback onDelete; const SwipeableListTile({ Key? key, required this.title, required this.subtitle, required this.onDelete, }) : super(key: key); @override Widget build(BuildContext context) { return Dismissible( key: Key(title), background: Container( color: Colors.red, alignment: Alignment.centerRight, padding: EdgeInsets.only(right: 16), child: Icon( Icons.delete, color: Colors.white, ), ), direction: DismissDirection.endToStart, onDismissed: (direction) => onDelete(), child: ListTile( title: Text(title), subtitle: Text(subtitle), leading: CircleAvatar( child: Text(title[0]), ), ), ); } }
2. Selectable ListTile
class SelectableListTile extends StatefulWidget { @override _SelectableListTileState createState() => _SelectableListTileState(); } class _SelectableListTileState extends State<SelectableListTile> { bool isSelected = false; @override Widget build(BuildContext context) { return ListTile( selected: isSelected, selectedTileColor: Colors.blue[50], leading: Icon( isSelected ? Icons.check_circle : Icons.circle_outlined, color: isSelected ? Colors.blue : Colors.grey, ), title: Text('Selectable Item'), subtitle: Text('Tap to select'), onTap: () { setState(() { isSelected = !isSelected; }); }, ); } }
Best Practices
1. Consistent Styling
Maintain consistent styling across your ListTiles:
class ConsistentListTile extends StatelessWidget { static const double _horizontalPadding = 16.0; static const double _verticalPadding = 12.0; static const double _iconSize = 24.0; @override Widget build(BuildContext context) { return ListTile( contentPadding: EdgeInsets.symmetric( horizontal: _horizontalPadding, vertical: _verticalPadding, ), leading: Icon( Icons.star, size: _iconSize, ), title: Text( 'Consistent Style', style: Theme.of(context).textTheme.titleMedium, ), subtitle: Text( 'Using consistent padding and sizes', style: Theme.of(context).textTheme.bodyMedium, ), ); } }
2. Performance Optimization
Use const
constructors when possible:
const ListTile( leading: Icon(Icons.info), title: Text('Static Content'), subtitle: Text('This ListTile will not rebuild'), )
Common Issues and Solutions
Issue 1: Overflow Problems
Handle long text properly:
ListTile( title: Text( 'Very long title that might overflow', overflow: TextOverflow.ellipsis, ), subtitle: Text( 'Very long subtitle text that might overflow to multiple lines', maxLines: 2, overflow: TextOverflow.ellipsis, ), )
Issue 2: Dense Content
Use the dense
property for compact lists:
ListTile( dense: true, title: Text('Compact ListTile'), subtitle: Text('Uses less vertical space'), )
Conclusion
ListTile is a powerful widget that can be used to create various list layouts:
- Use consistent styling for better UX
- Implement custom layouts when needed
- Consider performance optimizations
- Handle overflow properly
- Add interactivity with gestures
Remember these key points:
- Keep styling consistent across your app
- Use appropriate padding and spacing
- Handle long content gracefully
- Consider accessibility
- Optimize performance where possible