image

17 Mar 2026

9K

35K

Building a Task List Widget with Swipe Action and Priority Tag in Flutter

Flutter, Google's UI toolkit for building natively compiled applications for mobile, web, and desktop from a single codebase, offers powerful widgets and a declarative approach to UI development. In this article, we'll walk through creating an interactive task list widget. This widget will feature swipe-to-dismiss actions for marking tasks as complete or deleting them, and visually distinct priority tags to highlight important tasks.

1. Defining the Task Model

First, let's define a simple data model for our tasks. This model will hold properties like a unique ID, title, description, a priority level, and a completion status.

Priority Enum

We'll start with an enumeration for task priorities.


enum Priority {
  low,
  medium,
  high,
}

Task Class

Next, the Task class:


class Task {
  final String id;
  String title;
  String description;
  Priority priority;
  bool isCompleted;

  Task({
    required this.id,
    required this.title,
    this.description = '',
    this.priority = Priority.low,
    this.isCompleted = false,
  });

  // For immutability and easy updates
  Task copyWith({
    String? id,
    String? title,
    String? description,
    Priority? priority,
    bool? isCompleted,
  }) {
    return Task(
      id: id ?? this.id,
      title: title ?? this.title,
      description: description ?? this.description,
      priority: priority ?? this.priority,
      isCompleted: isCompleted ?? this.isCompleted,
    );
  }
}

2. Designing the Task Item Widget

The task item widget will be responsible for displaying a single task's details, including its title, description, and a visual indicator for its priority. We'll use ListTile for the main layout and a custom Container for the priority tag.


import 'package:flutter/material.dart';
// Assuming task_model.dart contains the Task and Priority definitions
import 'package:your_project_name/task_model.dart'; 

class TaskItem extends StatelessWidget {
  final Task task;
  final VoidCallback? onToggleComplete;

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

  Color _getPriorityColor(Priority priority) {
    switch (priority) {
      case Priority.low:
        return Colors.blue.shade200;
      case Priority.medium:
        return Colors.orange.shade300;
      case Priority.high:
        return Colors.red.shade400;
      default:
        return Colors.grey;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 8.0),
      color: task.isCompleted ? Colors.grey.shade200 : Colors.white,
      elevation: 2,
      child: ListTile(
        leading: Checkbox(
          value: task.isCompleted,
          onChanged: (_) => onToggleComplete?.call(),
          activeColor: Colors.green,
        ),
        title: Text(
          task.title,
          style: TextStyle(
            decoration: task.isCompleted ? TextDecoration.lineThrough : TextDecoration.none,
            color: task.isCompleted ? Colors.grey.shade700 : Colors.black,
            fontWeight: FontWeight.w500,
          ),
        ),
        subtitle: task.description.isNotEmpty
            ? Text(
                task.description,
                style: TextStyle(
                  decoration: task.isCompleted ? TextDecoration.lineThrough : TextDecoration.none,
                  color: task.isCompleted ? Colors.grey.shade600 : Colors.black54,
                ),
              )
            : null,
        trailing: Container(
          padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
          decoration: BoxDecoration(
            color: _getPriorityColor(task.priority),
            borderRadius: BorderRadius.circular(5.0),
          ),
          child: Text(
            task.priority.name.toUpperCase(),
            style: const TextStyle(
              color: Colors.white,
              fontSize: 10,
              fontWeight: FontWeight.bold,
            ),
          ),
        ),
        onTap: () {
          // Future expansion: Could open a detail screen or edit dialog
        },
      ),
    );
  }
}

3. Implementing Swipe Actions with Dismissible

Flutter's Dismissible widget makes it incredibly easy to add swipe-to-dismiss functionality to list items. We'll wrap our TaskItem with Dismissible to allow users to swipe left to delete or swipe right to mark as complete.


import 'package:flutter/material.dart';
import 'package:your_project_name/task_model.dart';
import 'package:your_project_name/task_item.dart'; // Assuming TaskItem is in task_item.dart

class TaskListScreen extends StatefulWidget {
  const TaskListScreen({Key? key}) : super(key: key);

  @override
  State createState() => _TaskListScreenState();
}

class _TaskListScreenState extends State {
  // Sample data. In a real application, this would come from a database or API.
  final List _tasks = [
    Task(id: '1', title: 'Buy groceries', description: 'Milk, Eggs, Bread', priority: Priority.high),
    Task(id: '2', title: 'Finish Flutter article', priority: Priority.high, isCompleted: false),
    Task(id: '3', title: 'Call mom', priority: Priority.medium),
    Task(id: '4', title: 'Go for a run', priority: Priority.low),
  ];

  void _deleteTask(String id) {
    setState(() {
      _tasks.removeWhere((task) => task.id == id);
    });
  }

  void _toggleTaskComplete(String id) {
    setState(() {
      final index = _tasks.indexWhere((task) => task.id == id);
      if (index != -1) {
        _tasks[index] = _tasks[index].copyWith(isCompleted: !_tasks[index].isCompleted);
      }
    });
  }

  void _addNewTask() {
    setState(() {
      _tasks.add(Task(
        id: DateTime.now().millisecondsSinceEpoch.toString(),
        title: 'New Task ${_tasks.length + 1}',
        description: 'A newly added task',
        priority: Priority.low,
        isCompleted: false,
      ));
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('My Task List'),
        backgroundColor: Colors.blueAccent,
        foregroundColor: Colors.white, // For better contrast with blueAccent
      ),
      body: _tasks.isEmpty
          ? const Center(child: Text('No tasks yet! Add some.'))
          : ListView.builder(
              itemCount: _tasks.length,
              itemBuilder: (context, index) {
                final task = _tasks[index];
                return Dismissible(
                  key: Key(task.id), // Unique key is crucial for Dismissible
                  direction: DismissDirection.horizontal,
                  background: Container(
                    color: Colors.green,
                    alignment: Alignment.centerLeft,
                    padding: const EdgeInsets.symmetric(horizontal: 20.0),
                    child: const Icon(Icons.check, color: Colors.white, size: 30),
                  ),
                  secondaryBackground: Container(
                    color: Colors.red,
                    alignment: Alignment.centerRight,
                    padding: const EdgeInsets.symmetric(horizontal: 20.0),
                    child: const Icon(Icons.delete, color: Colors.white, size: 30),
                  ),
                  confirmDismiss: (direction) async {
                    if (direction == DismissDirection.endToStart) {
                      // Swiping left (end to start) to delete: show confirmation dialog
                      return await showDialog(
                        context: context,
                        builder: (BuildContext context) {
                          return AlertDialog(
                            title: const Text("Confirm Deletion"),
                            content: const Text("Are you sure you want to delete this task?"),
                            actions: [
                              TextButton(
                                onPressed: () => Navigator.of(context).pop(false),
                                child: const Text("Cancel"),
                              ),
                              TextButton(
                                onPressed: () => Navigator.of(context).pop(true),
                                child: const Text("Delete"),
                              ),
                            ],
                          );
                        },
                      );
                    }
                    // Swiping right (start to end) to mark as complete: no confirmation needed
                    return Future.value(true);
                  },
                  onDismissed: (direction) {
                    if (direction == DismissDirection.endToStart) {
                      _deleteTask(task.id);
                      ScaffoldMessenger.of(context).showSnackBar(
                        SnackBar(content: Text('"${task.title}" deleted')),
                      );
                    } else {
                      _toggleTaskComplete(task.id);
                      ScaffoldMessenger.of(context).showSnackBar(
                        SnackBar(
                            content: Text(
                                '"${task.title}" marked as ${task.isCompleted ? 'incomplete' : 'complete'}')),
                      );
                    }
                  },
                  child: TaskItem(
                    task: task,
                    onToggleComplete: () => _toggleTaskComplete(task.id),
                  ),
                );
              },
            ),
      floatingActionButton: FloatingActionButton(
        onPressed: _addNewTask,
        backgroundColor: Colors.blueAccent,
        foregroundColor: Colors.white,
        child: const Icon(Icons.add),
      ),
    );
  }
}

4. Integrating into the Main Application

Finally, let's wire everything up in our main.dart file to run our new task list application.


import 'package:flutter/material.dart';
import 'package:your_project_name/task_list_screen.dart'; // Assuming TaskListScreen is in task_list_screen.dart
// import 'package:your_project_name/task_model.dart'; // Not strictly needed here if TaskListScreen encapsulates it

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Task List',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
        appBarTheme: const AppBarTheme(
          backgroundColor: Colors.blueAccent,
          foregroundColor: Colors.white,
        ),
      ),
      home: const TaskListScreen(),
    );
  }
}

Conclusion

In this article, we've successfully built a dynamic task list widget in Flutter featuring key functionalities like priority tags and interactive swipe actions. We covered defining a data model, designing individual task items, and integrating the powerful Dismissible widget for an intuitive user experience.

This foundation can be expanded further by adding features such as task editing, persistent storage (using packages like shared_preferences or a database like SQLite/Hive), filtering/sorting tasks, and more advanced state management solutions (e.g., Provider, BLoC, Riverpod) 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