image

01 Apr 2026

9K

35K

Creating a Task Tracker Widget with Daily, Weekly, and Monthly Views in Flutter

Developing a robust task management application often requires a flexible and intuitive user interface. A common and highly effective pattern is to provide users with different time-based views of their tasks: daily, weekly, and monthly. This article will guide you through the process of building such a Task Tracker widget in Flutter, covering the essential components, data models, and view implementations.

Core Concepts and Data Model

Before diving into the UI, we need a simple data model for our tasks and a way to manage dates. For simplicity, we'll use a basic Task class and rely on Flutter's built-in state management (setState) for this demonstration. For production applications, consider solutions like Provider, BLoC, or Riverpod.

Task Data Model

Our Task model will include a title, description, due date, and a completion status.


// lib/models/task.dart
import 'package:flutter/material.dart';

class Task {
  final String id;
  String title;
  String description;
  DateTime dueDate;
  bool isCompleted;

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

  // Helper for creating dummy tasks
  static List generateDummyTasks() {
    return [
      Task(
        id: '1',
        title: 'Complete Flutter Article',
        dueDate: DateTime.now().add(const Duration(days: 0, hours: 2)),
      ),
      Task(
        id: '2',
        title: 'Review PRs',
        dueDate: DateTime.now().add(const Duration(days: 0, hours: 5)),
        isCompleted: true,
      ),
      Task(
        id: '3',
        title: 'Plan Weekly Sprint',
        dueDate: DateTime.now().add(const Duration(days: 2)),
      ),
      Task(
        id: '4',
        title: 'Call Client X',
        dueDate: DateTime.now().add(const Duration(days: 3)),
      ),
      Task(
        id: '5',
        title: 'Prepare Presentation',
        dueDate: DateTime.now().add(const Duration(days: 7)),
      ),
      Task(
        id: '6',
        title: 'Team Meeting',
        dueDate: DateTime.now().add(const Duration(days: 0, hours: 1)),
      ),
      Task(
        id: '7',
        title: 'Grocery Shopping',
        dueDate: DateTime.now().add(const Duration(days: 1)),
      ),
      Task(
        id: '8',
        title: 'Read a Book',
        dueDate: DateTime.now().subtract(const Duration(days: 1)),
      ),
    ];
  }
}

Date Utility Functions

Working with dates and ranges (like start/end of week/month) is crucial. Let's create a utility file for this.


// lib/utils/date_utils.dart
import 'package:intl/intl.dart';

extension DateTimeExtension on DateTime {
  DateTime get startOfDay => DateTime(year, month, day);
  DateTime get endOfDay => DateTime(year, month, day, 23, 59, 59);

  DateTime get startOfWeek {
    final DateTime now = startOfDay;
    // Monday is the first day of the week (ISO 8601)
    return now.subtract(Duration(days: now.weekday - 1));
  }

  DateTime get endOfWeek {
    final DateTime now = startOfDay;
    // Sunday is the last day of the week
    return now.add(Duration(days: DateTime.daysPerWeek - now.weekday));
  }

  DateTime get startOfMonth => DateTime(year, month, 1);
  DateTime get endOfMonth => DateTime(year, month + 1, 0, 23, 59, 59);

  bool isSameDay(DateTime other) {
    return year == other.year && month == other.month && day == other.day;
  }

  bool isSameWeek(DateTime other) {
    return startOfWeek.isSameDay(other.startOfWeek);
  }

  bool isSameMonth(DateTime other) {
    return year == other.year && month == other.month;
  }

  String format(String pattern) {
    return DateFormat(pattern).format(this);
  }
}

Main Task Tracker Widget Structure

Our main widget, TaskTrackerScreen, will manage the selected view (daily, weekly, monthly) and the currently displayed date. It will also hold our list of tasks and filter them based on the selected view and date.


// lib/screens/task_tracker_screen.dart
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

import '../models/task.dart';
import '../utils/date_utils.dart';
import '../widgets/daily_task_view.dart';
import '../widgets/weekly_task_view.dart';
import '../widgets/monthly_task_view.dart';

enum TaskView { daily, weekly, monthly }

class TaskTrackerScreen extends StatefulWidget {
  const TaskTrackerScreen({super.key});

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

class _TaskTrackerScreenState extends State {
  TaskView _currentView = TaskView.daily;
  DateTime _selectedDate = DateTime.now().startOfDay; // Always normalize to start of day
  List _tasks = Task.generateDummyTasks(); // Our dummy task data

  @override
  Widget build(BuildContext context) {
    List filteredTasks;

    switch (_currentView) {
      case TaskView.daily:
        filteredTasks = _tasks.where((task) => task.dueDate.isSameDay(_selectedDate)).toList();
        break;
      case TaskView.weekly:
        filteredTasks = _tasks.where((task) => task.dueDate.isSameWeek(_selectedDate)).toList();
        break;
      case TaskView.monthly:
        filteredTasks = _tasks.where((task) => task.dueDate.isSameMonth(_selectedDate)).toList();
        break;
    }

    // Sort tasks by due date
    filteredTasks.sort((a, b) => a.dueDate.compareTo(b.dueDate));

    return Scaffold(
      appBar: AppBar(
        title: const Text('Task Tracker'),
        bottom: PreferredSize(
          preferredSize: const Size.fromHeight(56.0),
          child: Column(
            children: [
              _buildViewSwitcher(),
              const SizedBox(height: 8.0),
            ],
          ),
        ),
      ),
      body: Column(
        children: [
          _buildDateNavigator(),
          Expanded(
            child: _buildCurrentView(filteredTasks),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // TODO: Implement add task functionality
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('Add Task functionality not implemented')),
          );
        },
        child: const Icon(Icons.add),
      ),
    );
  }

  Widget _buildViewSwitcher() {
    return SegmentedButton(
      segments: const [
        ButtonSegment(
          value: TaskView.daily,
          label: Text('Day'),
          icon: Icon(Icons.calendar_view_day),
        ),
        ButtonSegment(
          value: TaskView.weekly,
          label: Text('Week'),
          icon: Icon(Icons.calendar_view_week),
        ),
        ButtonSegment(
          value: TaskView.monthly,
          label: Text('Month'),
          icon: Icon(Icons.calendar_view_month),
        ),
      ],
      selected: {_currentView},
      onSelectionChanged: (Set newSelection) {
        setState(() {
          _currentView = newSelection.first;
          _selectedDate = DateTime.now().startOfDay; // Reset date to today when changing view
        });
      },
    );
  }

  Widget _buildDateNavigator() {
    String dateRangeText;
    Function() onPrev;
    Function() onNext;

    switch (_currentView) {
      case TaskView.daily:
        dateRangeText = _selectedDate.format('EEEE, MMM d, y');
        onPrev = () => setState(() => _selectedDate = _selectedDate.subtract(const Duration(days: 1)));
        onNext = () => setState(() => _selectedDate = _selectedDate.add(const Duration(days: 1)));
        break;
      case TaskView.weekly:
        final start = _selectedDate.startOfWeek;
        final end = _selectedDate.endOfWeek;
        dateRangeText = '${start.format('MMM d')} - ${end.format('MMM d, y')}';
        onPrev = () => setState(() => _selectedDate = _selectedDate.subtract(const Duration(days: 7)));
        onNext = () => setState(() => _selectedDate = _selectedDate.add(const Duration(days: 7)));
        break;
      case TaskView.monthly:
        dateRangeText = _selectedDate.format('MMMM y');
        onPrev = () => setState(() => _selectedDate = DateTime(_selectedDate.year, _selectedDate.month - 1));
        onNext = () => setState(() => _selectedDate = DateTime(_selectedDate.year, _selectedDate.month + 1));
        break;
    }

    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          IconButton(onPressed: onPrev, icon: const Icon(Icons.arrow_back_ios)),
          Expanded(
            child: Center(
              child: GestureDetector(
                onTap: () {
                  // Optional: Implement a date picker to jump to a specific date
                  // For daily, pick single day. For weekly/monthly, maybe pick a day
                  // and adjust to start of week/month.
                  ScaffoldMessenger.of(context).showSnackBar(
                    const SnackBar(content: Text('Date picker not implemented')),
                  );
                },
                child: Text(
                  dateRangeText,
                  style: Theme.of(context).textTheme.titleMedium,
                ),
              ),
            ),
          ),
          IconButton(onPressed: onNext, icon: const Icon(Icons.arrow_forward_ios)),
        ],
      ),
    );
  }

  Widget _buildCurrentView(List filteredTasks) {
    switch (_currentView) {
      case TaskView.daily:
        return DailyTaskView(selectedDate: _selectedDate, tasks: filteredTasks);
      case TaskView.weekly:
        return WeeklyTaskView(selectedDate: _selectedDate, tasks: filteredTasks);
      case TaskView.monthly:
        return MonthlyTaskView(selectedDate: _selectedDate, tasks: filteredTasks);
    }
  }
}

The _TaskTrackerScreenState manages the _currentView and _selectedDate. It filters the tasks and passes them down to the specific view widgets. The _buildViewSwitcher uses a SegmentedButton for a clean tab-like navigation, and _buildDateNavigator handles changing the current date/week/month.

Implementing the View Widgets

Now, let's create the individual view widgets. Each will be a StatelessWidget, receiving the filtered tasks and the relevant date information from the parent TaskTrackerScreen.

Daily Task View (DailyTaskView)

This view simply lists tasks for the selected day.


// lib/widgets/daily_task_view.dart
import 'package:flutter/material.dart';
import '../models/task.dart';
import 'task_list_item.dart'; // We'll create this helper widget

class DailyTaskView extends StatelessWidget {
  final DateTime selectedDate;
  final List tasks;

  const DailyTaskView({
    super.key,
    required this.selectedDate,
    required this.tasks,
  });

  @override
  Widget build(BuildContext context) {
    if (tasks.isEmpty) {
      return Center(
        child: Text('No tasks for today!', style: Theme.of(context).textTheme.titleMedium),
      );
    }
    return ListView.builder(
      itemCount: tasks.length,
      itemBuilder: (context, index) {
        final task = tasks[index];
        return TaskListItem(task: task);
      },
    );
  }
}

Weekly Task View (WeeklyTaskView)

The weekly view displays tasks grouped by day within the selected week. We'll iterate through the days of the week and display tasks for each.


// lib/widgets/weekly_task_view.dart
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

import '../models/task.dart';
import '../utils/date_utils.dart';
import 'task_list_item.dart';

class WeeklyTaskView extends StatelessWidget {
  final DateTime selectedDate;
  final List tasks;

  const WeeklyTaskView({
    super.key,
    required this.selectedDate,
    required this.tasks,
  });

  @override
  Widget build(BuildContext context) {
    final DateTime startOfWeek = selectedDate.startOfWeek;
    List dayWidgets = [];

    // Iterate through the 7 days of the week
    for (int i = 0; i < DateTime.daysPerWeek; i++) {
      final DateTime currentDay = startOfWeek.add(Duration(days: i));
      final List tasksForDay = tasks
          .where((task) => task.dueDate.isSameDay(currentDay))
          .toList()
        ..sort((a, b) => a.dueDate.compareTo(b.dueDate)); // Sort tasks by time

      dayWidgets.add(
        Padding(
          padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                currentDay.format('EEE, MMM d'),
                style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
              ),
              const Divider(),
              if (tasksForDay.isEmpty)
                Padding(
                  padding: const EdgeInsets.only(left: 8.0, top: 4.0),
                  child: Text('No tasks', style: Theme.of(context).textTheme.bodySmall),
                )
              else
                ...tasksForDay.map((task) => TaskListItem(task: task)).toList(),
            ],
          ),
        ),
      );
    }

    if (tasks.isEmpty) {
      return Center(
        child: Text('No tasks for this week!', style: Theme.of(context).textTheme.titleMedium),
      );
    }

    return ListView(children: dayWidgets);
  }
}

Monthly Task View (MonthlyTaskView)

Similar to the weekly view, the monthly view will group tasks by day within the selected month. For simplicity, we won't create a full calendar grid but rather a list of days with their respective tasks.


// lib/widgets/monthly_task_view.dart
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

import '../models/task.dart';
import '../utils/date_utils.dart';
import 'task_list_item.dart';

class MonthlyTaskView extends StatelessWidget {
  final DateTime selectedDate;
  final List tasks;

  const MonthlyTaskView({
    super.key,
    required this.selectedDate,
    required this.tasks,
  });

  @override
  Widget build(BuildContext context) {
    final DateTime startOfMonth = selectedDate.startOfMonth;
    final DateTime endOfMonth = selectedDate.endOfMonth;
    List dayWidgets = [];

    // Iterate through the days of the month
    for (int i = 0; i <= endOfMonth.day - startOfMonth.day; i++) {
      final DateTime currentDay = startOfMonth.add(Duration(days: i));
      final List tasksForDay = tasks
          .where((task) => task.dueDate.isSameDay(currentDay))
          .toList()
        ..sort((a, b) => a.dueDate.compareTo(b.dueDate)); // Sort tasks by time

      dayWidgets.add(
        Padding(
          padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                currentDay.format('EEE, MMM d'),
                style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
              ),
              const Divider(),
              if (tasksForDay.isEmpty)
                Padding(
                  padding: const EdgeInsets.only(left: 8.0, top: 4.0),
                  child: Text('No tasks', style: Theme.of(context).textTheme.bodySmall),
                )
              else
                ...tasksForDay.map((task) => TaskListItem(task: task)).toList(),
            ],
          ),
        ),
      );
    }

    if (tasks.isEmpty) {
      return Center(
        child: Text('No tasks for this month!', style: Theme.of(context).textTheme.titleMedium),
      );
    }

    return ListView(children: dayWidgets);
  }
}

Task List Item (TaskListItem)

A reusable widget to display individual task details.


// lib/widgets/task_list_item.dart
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../models/task.dart';

class TaskListItem extends StatelessWidget {
  final Task task;
  // TODO: Add callbacks for onTap, onToggleCompletion etc.

  const TaskListItem({super.key, required this.task});

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 0),
      elevation: 0.5,
      child: ListTile(
        leading: Checkbox(
          value: task.isCompleted,
          onChanged: (bool? value) {
            // TODO: Implement task completion toggle
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text('Toggle completion for "${task.title}"')),
            );
          },
        ),
        title: Text(
          task.title,
          style: TextStyle(
            decoration: task.isCompleted ? TextDecoration.lineThrough : TextDecoration.none,
            color: task.isCompleted ? Colors.grey : Colors.black,
          ),
        ),
        subtitle: Text(
          DateFormat('h:mm a').format(task.dueDate),
          style: TextStyle(
            decoration: task.isCompleted ? TextDecoration.lineThrough : TextDecoration.none,
          ),
        ),
        trailing: IconButton(
          icon: const Icon(Icons.edit),
          onPressed: () {
            // TODO: Implement edit task functionality
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text('Edit "${task.title}"')),
            );
          },
        ),
        onTap: () {
          // TODO: Navigate to task detail screen
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text('View details for "${task.title}"')),
          );
        },
      ),
    );
  }
}

Running the Application

Finally, set up your main.dart to run the TaskTrackerScreen.


// lib/main.dart
import 'package:flutter/material.dart';
import 'screens/task_tracker_screen.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Task Tracker App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        useMaterial3: true,
      ),
      home: const TaskTrackerScreen(),
    );
  }
}

Conclusion

In this article, we've built a flexible Task Tracker widget in Flutter that allows users to view their tasks across daily, weekly, and monthly perspectives. We covered the creation of a task data model, essential date utility functions, and the structure for a main screen that orchestrates the different views. Each view widget (DailyTaskView, WeeklyTaskView, MonthlyTaskView) demonstrates how to filter and display tasks relevant to its specific time frame.

This implementation provides a solid foundation. For a production-ready application, you would extend this by:

  • Integrating a more robust state management solution (Provider, BLoC, Riverpod).
  • Implementing functionality to add, edit, delete, and mark tasks as complete.
  • Persisting tasks using local storage (e.g., Hive, SQLite) or a remote database.
  • Adding animations and transitions for a smoother user experience.
  • Enhancing the monthly view with a proper calendar grid display.

By following these steps, you can create a highly functional and user-friendly task management interface in your Flutter 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