image

19 Mar 2026

9K

35K

Building a Goal Tracker Widget with Daily, Weekly, and Monthly Views in Flutter

Goal tracking is a fundamental aspect of personal development and productivity applications. Building an interactive goal tracker widget in Flutter that offers daily, weekly, and monthly views can significantly enhance user engagement and provide insightful progress visualization. This article will guide you through creating such a widget, focusing on a clean architecture and dynamic view switching.

Core Concepts

Before diving into the code, let's outline the core concepts:

  1. Data Model: Define classes for goals and their progress entries to structure our data effectively.
  2. State Management: We'll use Provider for simple and robust state management, allowing our UI to react to data changes.
  3. View Switching: Implement a mechanism to toggle between daily, weekly, and monthly views.
  4. Dynamic UI: Render different widgets based on the selected view mode, fetching and displaying relevant data.

1. Data Model

First, let's define our data structures for a Goal and a ProgressEntry. A goal will have a name, a target, and a unit, while progress entries will track achievements for a specific goal on a given date.


// lib/models/goal.dart
class Goal {
  final String id;
  String name;
  double targetValue;
  String unit; // e.g., "hours", "pages", "times"

  Goal({required this.id, required this.name, required this.targetValue, required this.unit});
}

// lib/models/progress_entry.dart
class ProgressEntry {
  final String goalId;
  final DateTime date;
  double valueAchieved;

  ProgressEntry({required this.goalId, required this.date, required this.valueAchieved});
}

2. State Management with GoalManager

We'll create a GoalManager class using ChangeNotifier to hold our goals and progress, providing methods to add, update, and retrieve data. This class will be exposed via Provider.


// lib/providers/goal_manager.dart
import 'package:flutter/foundation.dart';
import '../models/goal.dart';
import '../models/progress_entry.dart';

class GoalManager with ChangeNotifier {
  final List<Goal> _goals = [];
  final List<ProgressEntry> _progressEntries = [];

  List<Goal> get goals => _goals;

  void addGoal(Goal goal) {
    _goals.add(goal);
    notifyListeners();
  }

  void addProgress(String goalId, DateTime date, double value) {
    // Check if there's an existing entry for the same goal and date
    final existingEntryIndex = _progressEntries.indexWhere(
      (entry) => entry.goalId == goalId &&
                 entry.date.year == date.year &&
                 entry.date.month == date.month &&
                 entry.date.day == date.day,
    );

    if (existingEntryIndex != -1) {
      _progressEntries[existingEntryIndex].valueAchieved += value;
    } else {
      _progressEntries.add(ProgressEntry(goalId: goalId, date: date, valueAchieved: value));
    }
    notifyListeners();
  }

  double getProgressForDate(String goalId, DateTime date) {
    return _progressEntries
        .where((entry) =>
            entry.goalId == goalId &&
            entry.date.year == date.year &&
            entry.date.month == date.month &&
            entry.date.day == date.day)
        .fold(0.0, (sum, entry) => sum + entry.valueAchieved);
  }

  double getProgressForWeek(String goalId, DateTime dateInWeek) {
    final startOfWeek = dateInWeek.subtract(Duration(days: dateInWeek.weekday - 1)); // Monday
    final endOfWeek = startOfWeek.add(const Duration(days: 6)); // Sunday

    return _progressEntries
        .where((entry) =>
            entry.goalId == goalId &&
            entry.date.isAfter(startOfWeek.subtract(const Duration(days: 1))) && // Inclusive start
            entry.date.isBefore(endOfWeek.add(const Duration(days: 1)))) // Inclusive end
        .fold(0.0, (sum, entry) => sum + entry.valueAchieved);
  }

  double getProgressForMonth(String goalId, DateTime dateInMonth) {
    return _progressEntries
        .where((entry) =>
            entry.goalId == goalId &&
            entry.date.year == dateInMonth.year &&
            entry.date.month == dateInMonth.month)
        .fold(0.0, (sum, entry) => sum + entry.valueAchieved);
  }
}

3. The Main Goal Tracker Widget

Our main widget, GoalTrackerWidget, will host the view selector and display the appropriate daily, weekly, or monthly view. We'll use a StatefulWidget to manage the selected view mode.


// lib/widgets/goal_tracker_widget.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/goal_manager.dart';
import '../models/goal.dart';
import '../models/progress_entry.dart'; // Ensure this is imported for usage example

enum ViewMode { daily, weekly, monthly }

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

  @override
  State<GoalTrackerWidget> createState() => _GoalTrackerWidgetState();
}

class _GoalTrackerWidgetState extends State<GoalTrackerWidget> {
  ViewMode _selectedViewMode = ViewMode.daily;
  DateTime _currentDate = DateTime.now();

  @override
  void initState() {
    super.initState();
    // Example: Add some initial goals for demonstration
    WidgetsBinding.instance.addPostFrameCallback((_) {
      final goalManager = Provider.of<GoalManager>(context, listen: false);
      if (goalManager.goals.isEmpty) {
        goalManager.addGoal(Goal(id: 'read', name: 'Read Book', targetValue: 30, unit: 'minutes'));
        goalManager.addGoal(Goal(id: 'exercise', name: 'Exercise', targetValue: 60, unit: 'minutes'));
      }
    });
  }

  Widget _buildViewSelector() {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceAround,
      children: ViewMode.values.map((mode) {
        return ElevatedButton(
          onPressed: () {
            setState(() {
              _selectedViewMode = mode;
            });
          },
          style: ElevatedButton.styleFrom(
            backgroundColor: _selectedViewMode == mode ? Colors.blue : Colors.grey,
            foregroundColor: Colors.white,
          ),
          child: Text(mode.toString().split('.').last.capitalize()),
        );
      }).toList(),
    );
  }

  Widget _buildCurrentView() {
    switch (_selectedViewMode) {
      case ViewMode.daily:
        return DailyGoalView(date: _currentDate);
      case ViewMode.weekly:
        return WeeklyGoalView(dateInWeek: _currentDate);
      case ViewMode.monthly:
        return MonthlyGoalView(dateInMonth: _currentDate);
      default:
        return const Text('Select a view mode');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        _buildViewSelector(),
        const SizedBox(height: 16),
        Expanded(child: _buildCurrentView()),
      ],
    );
  }
}

// Helper extension for capitalization
extension StringExtension on String {
    String capitalize() {
      return "${this[0].toUpperCase()}${substring(1)}";
    }
}

4. Implementing Daily, Weekly, and Monthly Views

Now, let's create the individual view widgets. Each view will consume the GoalManager to display relevant data.

DailyGoalView

This view will show all goals and allow users to input today's progress for each.


// lib/widgets/daily_goal_view.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/goal_manager.dart';

class DailyGoalView extends StatelessWidget {
  final DateTime date;
  const DailyGoalView({Key? key, required this.date}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final goalManager = Provider.of<GoalManager>(context);

    return ListView.builder(
      itemCount: goalManager.goals.length,
      itemBuilder: (context, index) {
        final goal = goalManager.goals[index];
        final progressToday = goalManager.getProgressForDate(goal.id, date);
        TextEditingController controller = TextEditingController(text: '');

        return Card(
          margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  goal.name,
                  style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                ),
                const SizedBox(height: 8),
                Text('Target: ${goal.targetValue} ${goal.unit}'),
                Text('Progress Today: ${progressToday} ${goal.unit}'),
                const SizedBox(height: 8),
                Row(
                  children: [
                    Expanded(
                      child: TextField(
                        controller: controller,
                        keyboardType: TextInputType.number,
                        decoration: InputDecoration(
                          hintText: 'Add progress (${goal.unit})',
                          border: const OutlineInputBorder(),
                          isDense: true,
                        ),
                      ),
                    ),
                    const SizedBox(width: 8),
                    ElevatedButton(
                      onPressed: () {
                        final value = double.tryParse(controller.text);
                        if (value != null && value > 0) {
                          goalManager.addProgress(goal.id, date, value);
                          controller.clear();
                        }
                      },
                      child: const Text('Add'),
                    ),
                  ],
                ),
              ],
            ),
          ),
        );
      },
    );
  }
}

WeeklyGoalView

This view will summarize the progress for each goal over the current week.


// lib/widgets/weekly_goal_view.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/goal_manager.dart';

class WeeklyGoalView extends StatelessWidget {
  final DateTime dateInWeek;
  const WeeklyGoalView({Key? key, required this.dateInWeek}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final goalManager = Provider.of<GoalManager>(context);
    final startOfWeek = dateInWeek.subtract(Duration(days: dateInWeek.weekday - 1)); // Monday

    return ListView.builder(
      itemCount: goalManager.goals.length,
      itemBuilder: (context, index) {
        final goal = goalManager.goals[index];
        final progressThisWeek = goalManager.getProgressForWeek(goal.id, dateInWeek);

        return Card(
          margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  goal.name,
                  style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                ),
                const SizedBox(height: 8),
                Text('Target (per week): ${goal.targetValue * 7} ${goal.unit}'), // Assuming target is daily
                Text('Progress This Week: ${progressThisWeek} ${goal.unit}'),
                // Could add a mini-chart or daily breakdown here
              ],
            ),
          ),
        );
      },
    );
  }
}

MonthlyGoalView

This view will summarize the progress for each goal over the current month.


// lib/widgets/monthly_goal_view.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/goal_manager.dart';

class MonthlyGoalView extends StatelessWidget {
  final DateTime dateInMonth;
  const MonthlyGoalView({Key? key, required this.dateInMonth}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final goalManager = Provider.of<GoalManager>(context);

    return ListView.builder(
      itemCount: goalManager.goals.length,
      itemBuilder: (context, index) {
        final goal = goalManager.goals[index];
        final progressThisMonth = goalManager.getProgressForMonth(goal.id, dateInMonth);
        final daysInMonth = DateTime(dateInMonth.year, dateInMonth.month + 1, 0).day; // Get last day of month

        return Card(
          margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  goal.name,
                  style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                ),
                const SizedBox(height: 8),
                Text('Target (per month): ${goal.targetValue * daysInMonth} ${goal.unit}'), // Assuming target is daily
                Text('Progress This Month: ${progressThisMonth} ${goal.unit}'),
                // Could add a grid of weeks or a summary chart here
              ],
            ),
          ),
        );
      },
    );
  }
}

5. Integrating into a Flutter App

Finally, you need to wrap your app or a portion of it with a ChangeNotifierProvider to make the GoalManager accessible to the widgets.


// lib/main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'providers/goal_manager.dart';
import 'widgets/goal_tracker_widget.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => GoalManager(),
      child: MaterialApp(
        title: 'Goal Tracker',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: Scaffold(
          appBar: AppBar(
            title: const Text('Goal Tracker Dashboard'),
          ),
          body: const GoalTrackerWidget(),
        ),
      ),
    );
  }
}

Conclusion

You've now built a functional goal tracker widget in Flutter with daily, weekly, and monthly views. This solution leverages Provider for state management and separates concerns by creating distinct widgets for each view mode. This foundation can be expanded upon with features like:

  • Persistence: Save goals and progress using local storage (e.g., shared_preferences, sqflite, or Hive) or a backend service.
  • Customization: Allow users to add, edit, or delete goals.
  • Visualizations: Integrate charting libraries (e.g., fl_chart) to display progress graphically.
  • Date Navigation: Add controls to navigate between different days, weeks, or months.
  • Notifications: Remind users to log their daily progress.

By following this guide, you have a robust starting point for creating powerful and intuitive goal-tracking experiences 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