image

11 Feb 2026

9K

35K

Building an Event Countdown Timer Widget with Notification Reminders in Flutter

Creating engaging and functional applications often involves interactive elements that keep users informed and engaged. A common requirement for event-based applications is a countdown timer that not only displays the remaining time but also provides timely reminders. This article will guide you through building a robust Flutter widget that serves as an event countdown timer, integrated with local notification reminders.

Prerequisites

  • Basic understanding of Flutter development.
  • Familiarity with Dart programming.

1. Setting Up the Project and Dependencies

First, create a new Flutter project if you haven't already. We'll need the flutter_local_notifications package to handle local notifications.


flutter create event_countdown_app
cd event_countdown_app

Add the following dependency to your pubspec.yaml file:


dependencies:
  flutter:
    sdk: flutter
  flutter_local_notifications: ^17.1.0
  timezone: ^0.9.2 # Required for scheduling notifications in specific timezones
  rxdart: ^0.28.0 # Optional, for notification stream listeners

Run flutter pub get to fetch the new dependencies.

Platform-Specific Setup for Notifications:

  • Android: No special setup required beyond the dependency.
  • iOS: Add the following to your ios/Runner/AppDelegate.swift file (if using Swift):

import UIKit
import Flutter
import flutter_local_notifications

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    FlutterLocalNotificationsPlugin.set ';application:didFinishLaunchingWithOptions:' with: self
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

2. Event Data Model

Let's define a simple data model for our event. This will include the event title and its scheduled date/time.


import 'package:flutter/foundation.dart';

class Event {
  final String id;
  final String title;
  final DateTime eventDateTime;

  Event({
    required this.id,
    required this.title,
    required this.eventDateTime,
  });
}

3. Notification Service

This service will handle the initialization of flutter_local_notifications and the scheduling of reminders.


import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:timezone/timezone.dart' as tz;
import 'package:timezone/data/latest.dart' as tzdata;
import 'package:rxdart/rxdart.dart'; // For listening to notifications

class NotificationService {
  static final _notifications = FlutterLocalNotificationsPlugin();
  static final onNotifications = BehaviorSubject<String?>();

  static Future init({bool initScheduled = false}) async {
    tzdata.initializeUsaAndCanada(); // Or any other region relevant to your users
    tz.setLocalLocation(tz.getLocation('America/New_York')); // Set your local timezone

    const android = AndroidInitializationSettings('@mipmap/ic_launcher');
    const iOS = DarwinInitializationSettings();
    const settings = InitializationSettings(android: android, iOS: iOS);
    await _notifications.initialize(
      settings,
      onDidReceiveNotificationResponse: (notificationResponse) async {
        onNotifications.add(notificationResponse.payload);
      },
    );
  }

  static Future _notificationDetails() async {
    return const NotificationDetails(
      android: AndroidNotificationDetails(
        'channel id',
        'channel name',
        channelDescription: 'channel description',
        importance: Importance.max,
        priority: Priority.high,
        ticker: 'ticker',
      ),
      iOS: DarwinNotificationDetails(),
    );
  }

  static Future showNotification({
    int id = 0,
    String? title,
    String? body,
    String? payload,
  }) async =>
      _notifications.show(
        id,
        title,
        body,
        await _notificationDetails(),
        payload: payload,
      );

  static Future scheduleNotification({
    required int id,
    required String title,
    required String body,
    required DateTime scheduledDate,
    String? payload,
  }) async {
    await _notifications.zonedSchedule(
      id,
      title,
      body,
      tz.TZDateTime.from(scheduledDate, tz.local),
      await _notificationDetails(),
      androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
      uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime,
      matchDateTimeComponents: DateTimeComponents.dateAndTime,
      payload: payload,
    );
  }

  static void cancelNotification(int id) async {
    await _notifications.cancel(id);
  }

  static void cancelAllNotifications() async {
    await _notifications.cancelAll();
  }
}

4. Countdown Timer Widget

This widget will display the remaining time until the event. It will update every second.


import 'dart:async';
import 'package:flutter/material.dart';
import 'package:event_countdown_app/event_model.dart';

class CountdownTimerWidget extends StatefulWidget {
  final Event event;

  const CountdownTimerWidget({Key? key, required this.event}) : super(key: key);

  @override
  _CountdownTimerWidgetState createState() => _CountdownTimerWidgetState();
}

class _CountdownTimerWidgetState extends State<CountdownTimerWidget> {
  late Timer _timer;
  Duration _remainingDuration = Duration.zero;

  @override
  void initState() {
    super.initState();
    _startTimer();
  }

  @override
  void dispose() {
    _timer.cancel();
    super.dispose();
  }

  void _startTimer() {
    _updateRemainingDuration(); // Initial update

    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      if (mounted) {
        _updateRemainingDuration();
      }
    });
  }

  void _updateRemainingDuration() {
    final now = DateTime.now();
    setState(() {
      _remainingDuration = widget.event.eventDateTime.difference(now);
    });
  }

  String _formatDuration(Duration duration) {
    if (duration.isNegative) {
      return "Event passed!";
    }

    String twoDigits(int n) => n.toString().padLeft(2, '0');
    String days = duration.inDays.toString();
    String hours = twoDigits(duration.inHours.remainder(24));
    String minutes = twoDigits(duration.inMinutes.remainder(60));
    String seconds = twoDigits(duration.inSeconds.remainder(60));

    return "${days}d ${hours}h ${minutes}m ${seconds}s";
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(
          widget.event.title,
          style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 10),
        Text(
          _formatDuration(_remainingDuration),
          style: const TextStyle(fontSize: 36, color: Colors.blueAccent),
        ),
      ],
    );
  }
}

5. Integrating It All in main.dart

Now, let's put everything together. We'll initialize the notification service, define an event, and schedule reminders for it.


import 'package:flutter/material.dart';
import 'package:event_countdown_app/event_model.dart';
import 'package:event_countdown_app/countdown_timer_widget.dart';
import 'package:event_countdown_app/notification_service.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await NotificationService.init(initScheduled: true);
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Event Countdown',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const EventCountdownScreen(),
    );
  }
}

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

  @override
  State<EventCountdownScreen> createState() => _EventCountdownScreenState();
}

class _EventCountdownScreenState extends State<EventCountdownScreen> {
  final Event upcomingEvent = Event(
    id: '1',
    title: 'Flutter Connect Conference',
    eventDateTime: DateTime.now().add(const Duration(days: 7, hours: 10, minutes: 30)),
  );

  @override
  void initState() {
    super.initState();
    _scheduleEventReminders();
    _listenToNotifications();
  }

  void _scheduleEventReminders() {
    // Schedule a reminder 24 hours before
    final reminder1Day = upcomingEvent.eventDateTime.subtract(const Duration(days: 1));
    if (reminder1Day.isAfter(DateTime.now())) {
      NotificationService.scheduleNotification(
        id: int.parse(upcomingEvent.id), // Unique ID for the event
        title: 'Reminder: ${upcomingEvent.title}',
        body: 'Your event is starting in 1 day!',
        scheduledDate: reminder1Day,
        payload: 'event_reminder_1d_${upcomingEvent.id}',
      );
    }

    // Schedule a reminder 1 hour before
    final reminder1Hour = upcomingEvent.eventDateTime.subtract(const Duration(hours: 1));
    if (reminder1Hour.isAfter(DateTime.now())) {
      NotificationService.scheduleNotification(
        id: int.parse(upcomingEvent.id) + 1, // Another unique ID for this reminder
        title: 'Reminder: ${upcomingEvent.title}',
        body: 'Your event is starting in 1 hour!',
        scheduledDate: reminder1Hour,
        payload: 'event_reminder_1h_${upcomingEvent.id}',
      );
    }
  }

  void _listenToNotifications() {
    NotificationService.onNotifications.listen((payload) {
      if (payload != null) {
        // Handle notification tap, e.g., navigate to event details screen
        print('Notification tapped with payload: $payload');
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('Notification tapped! Payload: $payload')),
        );
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Event Countdown Timer'),
      ),
      body: Center(
        child: CountdownTimerWidget(event: upcomingEvent),
      ),
    );
  }
}

Explanation of Key Components:

  • Event Model: A simple class to hold event details like title and its specific DateTime.
  • NotificationService:
    • Initializes FlutterLocalNotificationsPlugin with platform-specific settings.
    • tzdata.initializeUsaAndCanada() and tz.setLocalLocation() are crucial for scheduling notifications that respect the device's timezone, preventing issues with daylight saving or timezone changes.
    • scheduleNotification uses _notifications.zonedSchedule() to set a precise notification for a future date and time.
    • onNotifications is a BehaviorSubject from rxdart that allows listening to notification taps from anywhere in the app, facilitating navigation or specific actions upon interaction.
  • CountdownTimerWidget:
    • This is a StatefulWidget that holds a Timer.
    • The _timer is set to update the _remainingDuration every second.
    • _updateRemainingDuration() calculates the difference between the current time and the event time.
    • _formatDuration() converts the Duration object into a human-readable string (e.g., "7d 10h 30m 00s").
    • dispose() ensures the timer is cancelled to prevent memory leaks when the widget is removed from the widget tree.
  • EventCountdownScreen:
    • In initState, it calls _scheduleEventReminders() to set up notifications for the upcomingEvent. We've added two example reminders: one day and one hour before the event.
    • It also calls _listenToNotifications() to react when a user taps on a notification.
    • The screen displays the CountdownTimerWidget for the defined event.

Conclusion

You have now successfully built a Flutter application that displays a dynamic countdown timer for an event and schedules local notification reminders for that event. This solution provides a robust foundation that can be extended to support multiple events, custom reminder intervals, persistent storage for events, and more sophisticated UI designs. Remember to thoroughly test notifications on both Android and iOS devices, especially concerning exact scheduling and background execution.

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