Back to Posts

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:

  1. Use consistent styling for better UX
  2. Implement custom layouts when needed
  3. Consider performance optimizations
  4. Handle overflow properly
  5. 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