Building a Feature-Rich Task List Widget in Flutter with Tags, Priority, and Swipe Actions

12 Apr 2026

9K

35K

Building a Feature-Rich Task List Widget in Flutter with Tags, Priority, and Swipe Actions

Creating a highly functional and intuitive task list is a common requirement for many mobile applications. Flutter, with its expressive UI toolkit, makes it straightforward to build such components. This article will guide you through the process of developing a sophisticated task list widget that incorporates tags, priority levels, and engaging swipe-to-action functionalities.

1. Defining the Task Data Model

First, we need a robust data model to represent our tasks. Each task will have a unique identifier, a title, an optional description, a list of tags, a priority level, and a completion status.


import 'package:flutter/material.dart'; // Required for @required

enum Priority { low, medium, high }

class Task {
  final String id;
  final String title;
  final String description;
  final List<String> tags;
  Priority priority;
  bool isCompleted;

  Task({
    @required this.id,
    @required this.title,
    this.description = '',
    this.tags = const [],
    this.priority = Priority.medium,
    this.isCompleted = false,
  });

  // A simple copyWith method for immutability (useful for state management)
  Task copyWith({
    String id,
    String title,
    String description,
    List<String> tags,
    Priority priority,
    bool isCompleted,
  }) {
    return Task(
      id: id ?? this.id,
      title: title ?? this.title,
      description: description ?? this.description,
      tags: tags ?? this.tags,
      priority: priority ?? this.priority,
      isCompleted: isCompleted ?? this.isCompleted,
    );
  }
}

2. Crafting the Individual Task Item Widget

Next, we'll create a widget that displays a single task, visually representing its tags and priority. We'll use a ListTile as the base and add custom elements.


import 'package:flutter/material.dart';
import 'task_model.dart'; // Assuming task_model.dart contains the Task class

class TaskItem extends StatelessWidget {
  final Task task;
  final VoidCallback onTap;

  const TaskItem({
    Key key,
    @required this.task,
    this.onTap,
  }) : super(key: key);

  Color _getPriorityColor(Priority priority) {
    switch (priority) {
      case Priority.low:
        return Colors.blue[300];
      case Priority.medium:
        return Colors.orange[300];
      case Priority.high:
        return Colors.red[300];
      default:
        return Colors.grey;
    }
  }

  IconData _getPriorityIcon(Priority priority) {
    switch (priority) {
      case Priority.low:
        return Icons.arrow_downward;
      case Priority.medium:
        return Icons.sort;
      case Priority.high:
        return Icons.arrow_upward;
      default:
        return Icons.priority_high;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
      elevation: 2,
      child: ListTile(
        onTap: onTap,
        leading: Icon(
          task.isCompleted ? Icons.check_circle : Icons.radio_button_unchecked,
          color: task.isCompleted ? Colors.green : Colors.grey,
        ),
        title: Text(
          task.title,
          style: TextStyle(
            decoration: task.isCompleted ? TextDecoration.lineThrough : null,
            fontStyle: task.isCompleted ? FontStyle.italic : null,
          ),
        ),
        subtitle: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            if (task.description.isNotEmpty)
              Padding(
                padding: const EdgeInsets.only(top: 4.0),
                child: Text(task.description, style: TextStyle(fontSize: 12)),
              ),
            if (task.tags.isNotEmpty)
              Padding(
                padding: const EdgeInsets.only(top: 8.0),
                child: Wrap(
                  spacing: 6.0,
                  runSpacing: 4.0,
                  children: task.tags
                      .map((tag) => Chip(
                            label: Text(tag, style: TextStyle(fontSize: 10)),
                            materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
                            visualDensity: VisualDensity.compact,
                          ))
                      .toList(),
                ),
              ),
          ],
        ),
        trailing: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(
              _getPriorityIcon(task.priority),
              color: _getPriorityColor(task.priority),
              size: 18,
            ),
            Text(
              task.priority.toString().split('.').last, // e.g., "high"
              style: TextStyle(
                fontSize: 10,
                color: _getPriorityColor(task.priority),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

3. Implementing Swipe Actions with Dismissible

The Dismissible widget is perfect for adding swipe-to-action functionality. We'll wrap our TaskItem with Dismissible to allow users to complete or delete tasks by swiping.


import 'package:flutter/material.dart';
import 'task_model.dart';
import 'task_item.dart'; // Assuming task_item.dart contains the TaskItem widget

class TaskListScreen extends StatefulWidget {
  @override
  _TaskListScreenState createState() => _TaskListScreenState();
}

class _TaskListScreenState extends State<TaskListScreen> {
  List<Task> _tasks = [
    Task(
      id: '1',
      title: 'Buy groceries',
      description: 'Milk, bread, eggs, apples',
      tags: ['Shopping', 'Home'],
      priority: Priority.high,
    ),
    Task(
      id: '2',
      title: 'Finish Flutter article',
      description: 'Write about widgets, tags, and swipe actions',
      tags: ['Work', 'Coding'],
      priority: Priority.high,
    ),
    Task(
      id: '3',
      title: 'Call mom',
      tags: ['Personal'],
      priority: Priority.medium,
    ),
    Task(
      id: '4',
      title: 'Workout',
      description: '30 min cardio',
      tags: ['Health'],
      priority: Priority.low,
      isCompleted: true,
    ),
  ];

  void _toggleTaskCompletion(String taskId) {
    setState(() {
      _tasks = _tasks.map((task) {
        return task.id == taskId ? task.copyWith(isCompleted: !task.isCompleted) : task;
      }).toList();
    });
  }

  void _deleteTask(String taskId) {
    setState(() {
      _tasks.removeWhere((task) => task.id == taskId);
    });
    // Optionally show a SnackBar for undo
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text("Task deleted"),
        duration: Duration(seconds: 2),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('My Tasks'),
      ),
      body: ListView.builder(
        itemCount: _tasks.length,
        itemBuilder: (context, index) {
          final task = _tasks[index];
          return Dismissible(
            key: ValueKey(task.id), // Unique key for Dismissible
            direction: DismissDirection.horizontal,
            background: Container(
              color: Colors.green,
              alignment: Alignment.centerLeft,
              padding: EdgeInsets.only(left: 20),
              child: Icon(Icons.check, color: Colors.white),
            ),
            secondaryBackground: Container(
              color: Colors.red,
              alignment: Alignment.centerRight,
              padding: EdgeInsets.only(right: 20),
              child: Icon(Icons.delete, color: Colors.white),
            ),
            onDismissed: (direction) {
              if (direction == DismissDirection.startToEnd) {
                // Swipe from left to right (Complete)
                _toggleTaskCompletion(task.id);
              } else if (direction == DismissDirection.endToStart) {
                // Swipe from right to left (Delete)
                _deleteTask(task.id);
              }
            },
            child: TaskItem(
              task: task,
              onTap: () => _toggleTaskCompletion(task.id), // Tap to toggle completion
            ),
          );
        },
      ),
    );
  }
}

4. Integrating into a Flutter Application

To see your task list in action, simply set TaskListScreen as the home widget in your main.dart file.


import 'package:flutter/material.dart';
import 'task_list_screen.dart'; // Assuming task_list_screen.dart contains TaskListScreen

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Task List App',
      theme: ThemeData(
        primarySwatch: Colors.deepPurple,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: TaskListScreen(),
    );
  }
}

Conclusion

In this article, we've built a powerful and interactive task list widget in Flutter. We started by defining a comprehensive data model for our tasks, then crafted a visually rich TaskItem widget to display priority and tags. Finally, we integrated swipe actions using the Dismissible widget to enable intuitive completion and deletion of tasks.

This foundation can be further extended by adding features such as task editing, filtering, searching, persistent storage (e.g., using shared_preferences or a local database like sqflite), and more sophisticated state management solutions like Provider or Bloc for larger applications.

Related Articles

Aug 02, 2026

Building a Flutter Notification Center with Grouping & Actions

Learn to create a sophisticated in-app notification center in Flutter, featuring grouping, swipe-to-dismiss, custom icons, and actionable buttons for enhanced u

Aug 01, 2026

Flutter Layout Tips: Using FractionallySizedBox, AspectRatio, ConstrainedBox for Adaptive UI

Master Flutter's FractionallySizedBox, AspectRatio, and ConstrainedBox to build truly adaptive UIs. Learn how to control widget sizing, maintain proportions, an

Aug 01, 2026

Flutter Product Quick View: Animation, Related Items, Badges

Learn to create a dynamic product quick view widget in Flutter, incorporating smooth animations, relevant related items, and eye-catching promo badges for an en