Documentation Index Fetch the complete documentation index at: https://docs.presentum.dev/llms.txt
Use this file to discover all available pages before exploring further.
Architecture
Presentum separates presentation logic into five clear layers:
Fetch candidates
Your app fetches presentations from Firebase Remote Config, APIs, or local sources.
Feed to engine
Convert to items and feed to Presentum using setCandidates or setCandidatesWithDiff.
Guards process
Engine runs all guards in sequence to determine eligibility, apply rules, and update state.
State committed
New state is committed and observers are notified.
Outlets rebuild
Outlets watching affected surfaces rebuild with new active items.
User interacts
User dismisses or converts, which records to storage and may trigger guard re-evaluation.
The five layers
1. Payloads Domain data - campaigns, tips, app updates
2. Guards Business logic - eligibility, targeting, scheduling
3. Engine State management - slots, transitions, history
4. Storage Persistence - impressions, dismissals, conversions
5. Outlets UI rendering - widgets without business logic
Key concepts
Surfaces
Where presentations appear. Named locations in your UI:
enum AppSurface with PresentumSurface {
homeTopBanner,
watchlistHeader,
popup;
}
Learn more ->
Payloads
What you show. Your domain objects:
class CampaignPayload extends PresentumPayload < AppSurface , CampaignVariant > {
final String id;
final int priority;
final Map < String , Object ?> metadata;
final List < PresentumOption > options;
}
Learn more ->
Items
Concrete decisions: “show this payload with this option on this surface”:
final item = CampaignPresentumItem (
payload : campaign,
option : campaign.options.first,
);
// Derived properties
item.id // "campaign-123::banner::homeTopBanner"
item.surface // AppSurface.homeTopBanner
item.variant // CampaignVariant.banner
item.priority // 100
Learn more ->
Slots
Each surface has one slot containing:
Active item - Currently displayed (or null)
Queue - FIFO list of items waiting
PresentumSlot (
surface : AppSurface .homeTopBanner,
active : campaignItem,
queue : [tipItem, alertItem],
)
Learn more ->
Guards
Business logic that decides what gets shown:
class CampaignGuard extends PresentumGuard {
@override
FutureOr < PresentumState > call (
storage, history, state, candidates, context,
) async {
// Apply your rules
return state;
}
}
Learn more ->
Data flow
Fetch candidates
Your app fetches presentations from Firebase, APIs, or local sources. final campaigns = await fetchCampaigns ();
Feed to engine
Convert to items and feed to Presentum. final items = campaigns. map ((c) => CampaignItem (
payload : c,
option : c.options.first,
)). toList ();
engine. setCandidatesWithDiff ((state) => items);
Guards process
Engine runs all guards to determine eligibility. // In guard
for ( final candidate in candidates) {
if ( await isEligible (candidate)) {
state. setActive (candidate.surface, candidate);
}
}
State committed
New state is committed and observers notified.
Outlets rebuild
Outlets watching affected surfaces rebuild. PresentumOutlet (
surface : AppSurface .homeTopBanner,
builder : (context, item) => BannerWidget (item),
)
User interacts
User dismisses or converts, triggering new guard evaluation. await presentum. markDismissed (item);
// -> Guards re-run
// -> State updates
// -> Outlets rebuild
Production example
Here’s how a production app structures everything:
// Surfaces
enum CampaignSurface with PresentumSurface {
popup,
watchlistHeader,
watchlistFooter,
menuTile;
}
// Variants
enum CampaignVariant with PresentumVisualVariant {
fullscreenDialog,
dialog,
banner,
inline;
}
// Payload
class CampaignPayload extends PresentumPayload < CampaignSurface , CampaignVariant > {
factory CampaignPayload . fromJson ( Map < String , Object ?> json) { /* ... */ }
// ... fields
}
// Initialize
final presentum = Presentum (
storage : storage,
eventHandlers : [
PresentumStorageEventHandler (storage : storage),
],
guards : [
AppOpenedCountGuard ( /* ... */ ),
AppLifecycleChangedGuard ( /* ... */ ),
SyncStateWithCandidatesGuard (),
CampaignSchedulingGuard (eligibility),
RemoveIneligibleCampaignsGuard (eligibility),
],
);
// Provider fetches from Firebase Remote Config
final provider = CampaignProvider (
storage : storage,
engine : presentum.config.engine,
eligibility : eligibility,
remoteConfig : FirebaseRemoteConfig .instance,
);
See full implementation ->
Next steps
Surfaces Define UI locations
Payloads Create domain objects
Guards Build eligibility logic
Outlets Render presentations