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.
Overview
Guards are where your presentation business logic lives. This guide shows you how to build guards from simple to production-level complexity.
Basic guard
Start with a simple guard that sets the highest priority item as active:
final class FeatureSchedulingGuard
extends PresentumGuard<FeatureItem, AppSurface, AppVariant> {
FeatureSchedulingGuard({
required this.eligibilityResolver,
super.refresh,
});
final EligibilityResolver<FeatureItem> eligibilityResolver;
@override
Future<PresentumState<FeatureItem, AppSurface, AppVariant>> call(
PresentumStorage<AppSurface, AppVariant> storage,
List<PresentumHistoryEntry<FeatureItem, AppSurface, AppVariant>> history,
PresentumState$Mutable<FeatureItem, AppSurface, AppVariant> state,
List<FeatureItem> candidates,
Map<String, Object?> context,
) async {
// 1) Filter: if feature is gone, disabled, ineligible, or dismissed,
// exclude it from UI.
final filtered = <FeatureItem>[];
for (final item in candidates) {
// Check eligibility (time ranges, segments, etc.)
final isEligible = await eligibilityResolver.isEligible(item, context);
if (!isEligible) continue;
// Check if feature is dismissed
final dismissedAt = await storage.getDismissedAt(
item.id,
surface: item.surface,
variant: item.variant,
);
if (dismissedAt != null) continue;
filtered.add(item);
}
// 3) Project candidates -> slots (active + queue)
// This is what makes Settings rows and UI banners actually appear.
state.clearAll();
final bySurface = <AppSurface, List<FeatureItem>>{};
for (final item in filtered) {
(bySurface[item.surface] ??= <FeatureItem>[]).add(item);
}
for (final entry in bySurface.entries) {
final surface = entry.key;
final items = entry.value;
int stageOf(FeatureItem i) => i.stage ?? 0;
items.sort((a, b) {
final stageCmp = stageOf(a).compareTo(stageOf(b));
if (stageCmp != 0) return stageCmp;
return b.priority.compareTo(a.priority);
});
state.addAll(surface, items);
}
return state;
}
}
Production guard examples
See real implementations:
See all production guards
Complete guard implementations from a live app
See all reusable guards
Generic guards to sync state, remove ineligible candidates and simple eligibility scheduling