Skip to main content

What is storage?

Storage is the persistence layer for tracking presentation events. It records:
  • Impressions - When presentations are shown
  • Dismissals - When users close presentations
  • Conversions - When users take action
Guards and the engine use storage to make decisions about what to show.
Storage is optional but highly recommended. If not provided, Presentum uses NoOpPresentumStorage which does nothing and logs warnings (only visible when presentum.logs is enabled via --dart-define=presentum.logs=true). Without storage, impression tracking, cooldowns, and dismissal states won’t persist.Always provide a storage implementation in production.

Storage interface

Implement PresentumStorage<S, V>:

SharedPreferences implementation

Generic production implementation using SharedPreferences, that can be used for any presentum:
See full production storage ->
Per-surface-variant tracking lets the same campaign appear differently on different surfaces with independent impression counts.

In-memory implementation

For testing or session-only tracking, use built-in InMemoryPresentumStorage

Backend API implementation

Sync events to a backend:

No-op storage (default)

If you don’t provide a storage implementation, Presentum uses NoOpPresentumStorage:
NoOpPresentumStorage does nothing and returns default values:
  • getLastShown()null
  • getShownCount()0
  • getDismissedAt()null
  • All record*() methods → no-op with warning log
Don’t rely on NoOpPresentumStorage in production. Without storage, your guards can’t check impression counts, cooldowns, or dismissal states. Always provide a real storage implementation.

Storage event handler

Use PresentumStorageEventHandler to automatically record events:
Learn more about events ->

Best practices

Don’t rely on NoOpPresentumStorage. Always pass a real storage implementation:
getShownCount receives a period parameter. Only count impressions within that timeframe: dart final cutoff = DateTime.now().subtract(period); final recentCount = timestamps.where((t) => t.isAfter(cutoff)).length;
Avoid async initialization in initState. Use SharedPreferencesWithCache for synchronous access:
Use --dart-define=presentum.logs=true to see NoOpPresentumStorage warnings and other logs:
This helps catch missing storage implementations during development.

Next steps

Guards

Use storage in guards

Event system

Handle events with storage

Implementation guide

Step-by-step storage building