image

28 Dec 2025

9K

35K

Building a Notification Center Widget in Flutter

A notification center is a crucial component in modern applications, providing users with a centralized hub to view and manage important alerts, updates, and messages. Implementing such a feature in Flutter allows for a consistent and highly customizable user experience across different platforms. This article will guide you through building a robust and professional notification center widget in Flutter, covering data modeling, UI design, and state management.

Understanding the Core Concept

At its heart, a notification center displays a list of individual notifications. Each notification needs a defined structure and the ability to be interacted with (e.g., marked as read, dismissed). The entire system must efficiently manage the state of these notifications, allowing for dynamic additions, removals, and updates.

1. Defining the Notification Data Model

First, we need a data model to represent a single notification. This class will encapsulate all necessary information about an alert.


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

class AppNotification {
  final String id;
  final String title;
  final String body;
  final DateTime timestamp;
  bool isRead;

  AppNotification({
    required this.id,
    required this.title,
    required this.body,
    required this.timestamp,
    this.isRead = false,
  });

  // Factory constructor for creating a Notification from a map (e.g., JSON)
  factory AppNotification.fromJson(Map<String, dynamic> json) {
    return AppNotification(
      id: json['id'],
      title: json['title'],
      body: json['body'],
      timestamp: DateTime.parse(json['timestamp']),
      isRead: json['isRead'] ?? false,
    );
  }

  // Method for converting a Notification to a map (e.g., for storage)
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'title': title,
      'body': body,
      'timestamp': timestamp.toIso8601String(),
      'isRead': isRead,
    };
  }
}

2. Designing the Notification Item Widget

Next, we'll create a widget responsible for displaying an individual notification. This widget should visually distinguish between read and unread notifications and provide options for user interaction.


// lib/widgets/notification_item_widget.dart
import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; // For formatting dates
import '../models/notification_model.dart';

class NotificationItemWidget extends StatelessWidget {
  final AppNotification notification;
  final VoidCallback onMarkAsRead;
  final VoidCallback onDismiss;

  const NotificationItemWidget({
    Key? key,
    required this.notification,
    required this.onMarkAsRead,
    required this.onDismiss,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final textTheme = theme.textTheme;

    return Card(
      margin: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
      elevation: notification.isRead ? 0.5 : 2.0,
      color: notification.isRead ? Colors.grey[100] : theme.cardColor,
      child: InkWell(
        onTap: notification.isRead ? null : onMarkAsRead,
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Expanded(
                    child: Text(
                      notification.title,
                      style: textTheme.titleMedium?.copyWith(
                        fontWeight: notification.isRead ? FontWeight.normal : FontWeight.bold,
                        color: notification.isRead ? Colors.grey[700] : textTheme.titleMedium?.color,
                      ),
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                    ),
                  ),
                  Text(
                    DateFormat('MMM d, h:mm a').format(notification.timestamp),
                    style: textTheme.bodySmall?.copyWith(
                      color: Colors.grey[600],
                    ),
                  ),
                ],
              ),
              const SizedBox(height: 8.0),
              Text(
                notification.body,
                style: textTheme.bodyMedium?.copyWith(
                  color: notification.isRead ? Colors.grey[600] : textTheme.bodyMedium?.color,
                ),
                maxLines: 2,
                overflow: TextOverflow.ellipsis,
              ),
              if (!notification.isRead) ...[
                const SizedBox(height: 12.0),
                Align(
                  alignment: Alignment.bottomRight,
                  child: TextButton(
                    onPressed: onMarkAsRead,
                    child: const Text('MARK AS READ'),
                  ),
                ),
              ],
              if (notification.isRead) ...[
                const SizedBox(height: 12.0),
                Align(
                  alignment: Alignment.bottomRight,
                  child: TextButton(
                    onPressed: onDismiss,
                    child: const Text('DISMISS'),
                  ),
                ),
              ],
            ],
          ),
        ),
      ),
    );
  }
}

3. Implementing the Notification Center Logic (State Management)

For state management, we'll use a simple `ChangeNotifier` combined with the `provider` package. This `NotificationService` class will manage the list of notifications, and provide methods to add, remove, and update their status.


// lib/services/notification_service.dart
import 'package:flutter/material.dart';
import '../models/notification_model.dart';
import 'package:uuid/uuid.dart'; // For generating unique IDs

class NotificationService extends ChangeNotifier {
  final List<AppNotification> _notifications = [];
  final Uuid _uuid = Uuid();

  List<AppNotification> get notifications => List.unmodifiable(_notifications);

  List<AppNotification> get unreadNotifications =>
      _notifications.where((n) => !n.isRead).toList();

  int get unreadCount => unreadNotifications.length;

  void addNotification({
    required String title,
    required String body,
  }) {
    final newNotification = AppNotification(
      id: _uuid.v4(), // Generate a unique ID
      title: title,
      body: body,
      timestamp: DateTime.now(),
      isRead: false,
    );
    _notifications.insert(0, newNotification); // Add to the beginning
    notifyListeners();
  }

  void markNotificationAsRead(String id) {
    try {
      final notification = _notifications.firstWhere((n) => n.id == id);
      if (!notification.isRead) {
        notification.isRead = true;
        notifyListeners();
      }
    } catch (e) {
      // Notification not found, handle error or log
      debugPrint('Notification with ID $id not found.');
    }
  }

  void dismissNotification(String id) {
    _notifications.removeWhere((n) => n.id == id);
    notifyListeners();
  }

  void markAllAsRead() {
    for (var notification in _notifications) {
      notification.isRead = true;
    }
    notifyListeners();
  }

  void clearAll() {
    _notifications.clear();
    notifyListeners();
  }
}

Remember to add the `provider` and `uuid` packages to your `pubspec.yaml`:


dependencies:
  flutter:
    sdk: flutter
  provider: ^6.0.5
  uuid: ^4.3.3 # Or the latest version
  intl: ^0.18.1 # For date formatting

4. Constructing the Main Notification Center Widget

This widget will display the list of `NotificationItemWidget` instances and interact with the `NotificationService` to fetch and update notifications.


// lib/widgets/notification_center_widget.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../services/notification_service.dart';
import 'notification_item_widget.dart';

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

  @override
  Widget build(BuildContext context) {
    // Watch for changes in NotificationService
    return Consumer<NotificationService>(
      builder: (context, notificationService, child) {
        final notifications = notificationService.notifications;

        return Scaffold(
          appBar: AppBar(
            title: const Text('Notifications'),
            actions: [
              if (notifications.isNotEmpty)
                TextButton(
                  onPressed: notificationService.markAllAsRead,
                  child: const Text(
                    'Mark All as Read',
                    style: TextStyle(color: Colors.white),
                  ),
                ),
              if (notifications.isNotEmpty)
                IconButton(
                  icon: const Icon(Icons.clear_all, color: Colors.white),
                  tooltip: 'Clear All',
                  onPressed: notificationService.clearAll,
                ),
            ],
          ),
          body: notifications.isEmpty
              ? Center(
                  child: Text(
                    'No new notifications!',
                    style: Theme.of(context).textTheme.headlineSmall,
                  ),
                )
              : ListView.builder(
                  itemCount: notifications.length,
                  itemBuilder: (context, index) {
                    final notification = notifications[index];
                    return NotificationItemWidget(
                      notification: notification,
                      onMarkAsRead: () {
                        notificationService.markNotificationAsRead(notification.id);
                      },
                      onDismiss: () {
                        notificationService.dismissNotification(notification.id);
                      },
                    );
                  },
                ),
        );
      },
    );
  }
}

5. Integrating into Your Flutter App

To make the `NotificationService` available throughout your widget tree, you need to provide it using `ChangeNotifierProvider` at a suitable level, typically near the root of your application.


// lib/main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'services/notification_service.dart';
import 'widgets/notification_center_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) => NotificationService(),
      child: MaterialApp(
        title: 'Flutter Notification Center',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: const MyHomePage(),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    // Access the NotificationService instance
    final notificationService = Provider.of<NotificationService>(context);

    return Scaffold(
      appBar: AppBar(
        title: const Text('App Home'),
        actions: [
          Stack(
            children: [
              IconButton(
                icon: const Icon(Icons.notifications),
                onPressed: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => const NotificationCenterWidget(),
                    ),
                  );
                },
              ),
              if (notificationService.unreadCount > 0)
                Positioned(
                  right: 8,
                  top: 8,
                  child: Container(
                    padding: const EdgeInsets.all(2),
                    decoration: BoxDecoration(
                      color: Colors.red,
                      borderRadius: BorderRadius.circular(6),
                    ),
                    constraints: const BoxConstraints(
                      minWidth: 14,
                      minHeight: 14,
                    ),
                    child: Text(
                      '${notificationService.unreadCount}',
                      style: const TextStyle(
                        color: Colors.white,
                        fontSize: 8,
                      ),
                      textAlign: TextAlign.center,
                    ),
                  ),
                ),
            ],
          ),
        ],
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              'Welcome to the Home Page!',
              style: TextStyle(fontSize: 24),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                // Example of adding a new notification
                notificationService.addNotification(
                  title: 'New Update Available!',
                  body: 'Version 2.0.1 of our app is now available with new features and bug fixes.',
                );
                ScaffoldMessenger.of(context).showSnackBar(
                  const SnackBar(content: Text('New notification added!')),
                );
              },
              child: const Text('Add New Notification'),
            ),
            const SizedBox(height: 10),
            ElevatedButton(
              onPressed: () {
                notificationService.addNotification(
                  title: 'Important Security Alert',
                  body: 'Please review your account settings for recent unusual activity.',
                );
                ScaffoldMessenger.of(context).showSnackBar(
                  const SnackBar(content: Text('New notification added!')),
                );
              },
              child: const Text('Add Another Notification'),
            ),
          ],
        ),
      ),
    );
  }
}

Enhancements and Further Considerations

  • Persistence: For real-world applications, notifications should persist across app restarts. You can achieve this by saving the notification list to local storage (e.g., `shared_preferences`, `hive`, `sembast`) and loading it on app startup.
  • Push Notifications Integration: Integrate with Firebase Cloud Messaging (FCM) or other push notification services. When a push notification arrives, you can add it to your `NotificationService`.
  • Animations: Add subtle animations for when notifications are added, dismissed, or marked as read to improve the user experience.
  • Custom Actions: Extend `NotificationItemWidget` to support custom actions beyond just marking as read or dismissing, such as "View Details" or "Reply."
  • Filtering and Sorting: Implement options to filter notifications (e.g., show only unread) or sort them by timestamp, priority, etc.
  • Badging: Ensure the unread count displayed on the notification icon updates in real-time.

Conclusion

Building a notification center in Flutter provides a highly functional and customizable way to keep users informed. By following the modular approach outlined in this article – defining a clear data model, creating dedicated widgets for items and the center itself, and managing state with `ChangeNotifier` and `Provider` – you can develop a professional and scalable notification system for your Flutter applications. This foundation can then be expanded with persistence, push notification integration, and advanced UI/UX features to create a truly rich user experience.

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