image

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

May 14, 2026

Building a Multi-Event Countdown Timer Widget with Reminders, Notifications, Repeat, and Custom Labels in Flutter

Building a Multi-Event Countdown Timer Widget with Reminders, Notifications, Repeat, and Custom Labels in Flutter Countdown timers are essential in many applic

May 11, 2026

Unleashing Dynamic UIs: Flutter's Animation Prowess

Unleashing Dynamic UIs: Flutter's Animation Prowess for Slide & Scale Effects Flutter's declarative UI framework, combined with its powerful animation capabilit

May 11, 2026

Building a Product Detail Page Widget in Flutter with Related Items, Review Carousel, Promo Badges, and Quick Buy

Building a Product Detail Page Widget in Flutter with Related Items, Review Carousel, Promo Badges, and Quick Buy A well-designed Product Detail Page (PDP) is