notification_centre 0.0.5 copy "notification_centre: ^0.0.5" to clipboard
notification_centre: ^0.0.5 copied to clipboard

A lightweight, type-safe pub/sub library for Dart, inspired by Apple's `NotificationCenter` and Android's `EventBus`.

English | 中文 | Français | Deutsch | Español

NotificationCentre #

iOS Android Flutter Dart pub.dev

A lightweight, type-safe pub/sub library for Dart, inspired by Apple's NotificationCenter and Android's EventBus.

How to install #

Install the package from pub.dev:

flutter pub add notification_centre

Getting started #

Define a notification name once with its payload type:

const userSignedIn = NotificationName<String>('userSignedIn');

final token = NotificationCenter.shared.addObserver(userSignedIn, (userId) {
  print('Signed in: $userId');
});

NotificationCenter.shared.post(userSignedIn, 'user-123');

token.removeObserver();

Use NotificationCenter() when an isolated center is preferable, such as in a test or when injecting a dependency.

Payload types #

The generic type in NotificationName<T> is the type of value delivered to observers. T can be any Dart type.

Primitive values #

const userSignedIn = NotificationName<String>('userSignedIn');
const cartCountChanged = NotificationName<int>('cartCountChanged');
const loadingChanged = NotificationName<bool>('loadingChanged');
const downloadProgressChanged =
    NotificationName<double>('downloadProgressChanged');

NotificationCenter.shared.post(userSignedIn, 'user-123');
NotificationCenter.shared.post(cartCountChanged, 3);
NotificationCenter.shared.post(loadingChanged, true);
NotificationCenter.shared.post(downloadProgressChanged, 0.75);

Collections and maps #

const selectedItemsChanged =
    NotificationName<List<String>>('selectedItemsChanged');
const selectedIdsChanged =
    NotificationName<Set<int>>('selectedIdsChanged');
const settingsChanged =
    NotificationName<Map<String, Object?>>('settingsChanged');

NotificationCenter.shared.post(
  selectedItemsChanged,
  ['book', 'pen'],
);
NotificationCenter.shared.post(
  selectedIdsChanged,
  {10, 20},
);
NotificationCenter.shared.post(
  settingsChanged,
  {
    'theme': 'dark',
    'notificationsEnabled': true,
  },
);

Use a typed model instead of a map when the payload has a stable business structure. This gives fields meaningful names and lets the compiler check them.

Custom models #

class User {
  const User({
    required this.id,
    required this.displayName,
  });

  final String id;
  final String displayName;
}

const currentUserChanged =
    NotificationName<User>('currentUserChanged');

final token = NotificationCenter.shared.addObserver(
  currentUserChanged,
  (user) {
    print(user.displayName);
  },
);

NotificationCenter.shared.post(
  currentUserChanged,
  const User(id: 'user-123', displayName: 'Ada'),
);

Nullable payloads #

Add ? when null has an intentional meaning. For example, null can mean that there is no current user:

const currentUserChanged =
    NotificationName<User?>('currentUserChanged');

NotificationCenter.shared.post(currentUserChanged, null);
NotificationCenter.shared.post(
  currentUserChanged,
  const User(id: 'user-123', displayName: 'Ada'),
);

Events without data #

For an event that carries no data, use a small immutable event type:

class UserSignedOut {
  const UserSignedOut();
}

const userSignedOut =
    NotificationName<UserSignedOut>('userSignedOut');

NotificationCenter.shared.addObserver(userSignedOut, (_) {
  print('Signed out');
});

NotificationCenter.shared.post(
  userSignedOut,
  const UserSignedOut(),
);

Prefer a specific payload type over dynamic, Object?, or Map<String, Object?> when the notification has a known structure. Specific types preserve the compile-time safety provided by NotificationName<T>.

Delivery semantics #

Delivery is synchronous. post invokes matching callbacks in registration order and returns only after they finish.

Each post iterates over a snapshot of the observers present when delivery starts. An observer added by a callback begins receiving notifications on the next post. An observer removed by a callback remains part of the current delivery if it was present in that snapshot, but receives no later posts.

Token lifecycle #

addObserver returns an ObservationToken. Keep the token for as long as the observation is needed, then call token.removeObserver(). Removal is idempotent, so calling it repeatedly is safe. center.removeObserver(token) is an equivalent convenience.

Why typed names? #

NotificationName<T> connects each notification name to its payload at compile time. A NotificationName<String> cannot be posted with an int, and two names with the same text but different payload types are distinct.

This intentionally favors clear call sites and compiler-checked payloads over a string and userInfo-style design. The public API stays limited to notification names, centers, and observation tokens.

5
likes
160
points
175
downloads

Documentation

API reference

Publisher

verified publisherjejouejeux.com

Weekly Downloads

A lightweight, type-safe pub/sub library for Dart, inspired by Apple's `NotificationCenter` and Android's `EventBus`.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter

More

Packages that depend on notification_centre