Block Tab · Empty States

Block Tab — Permission + Empty

Two empty states: first-time user (no permission) with platform-aware inline steps, and returning user (permission granted, no blockers) with compact CTA. Both use the same !hasBlockingPermission check — catches first-time AND revoked permission.

STATE 1

First-Time / Permission Revoked

iOS · FamilyControls Mascot + inline steps + "Allow & continue"
7:47
Block distractions
Allow access to get started.
Screen Time access needed
One-time setup · ~10 seconds
1
iOS will ask for Screen Time access
2
Tap Allow on the system dialog
3
You're back here, ready to block
Allow & continue
Android · AccessibilityService Same layout, different steps + icon
7:47
Block distractions
Allow access to get started.
Accessibility access needed
One-time setup · ~15 seconds
1
Find LockInVibe in the list
2
Toggle the switch on
3
Tap Allow, then come back here
Allow & continue
STATE 2

Returning User · No Blockers

Permission Granted · Empty Real app header + compact CTA · same on both platforms
7:47

Block

No blockers yet
Create one to keep distracting apps locked.
Create blocker

Decision Logic

Flow
!hasBlockingPermission → State 1 (permission screen, catches both first-time AND revoked)
blockers.isEmpty() → State 2 (returning user, compact empty)
else → Blocker list
Same check, two cases
State 1 handles both first-time users (never granted) and returning users whose permission got revoked (turned off in settings, OS update wiped it, reinstall). Same UI for both — the user just needs to re-grant.
Platform differences
iOS: icon = shield-lock, title = "Screen Time access needed", steps explain FamilyControls dialog, button fires AuthorizationCenter.shared.requestAuthorization.
Android: icon = accessibility, title = "Accessibility access needed", steps explain system settings toggle, button fires Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS).
expect/actual pattern
UI composable is shared (commonMain). Only the permission card content (icon, title, subtitle, 3 step strings) and the button action differ per platform via expect/actual.

Component Specs

Mascot orb
80dp, same rendering as Home screen MascotOrb. HappyFace (round eyes + coral smile). No animations on this screen — static orb.
Permission card
Background: rgba(86,62,0,0.06). Radius: 14dp. Icon square: 34dp, radius 9dp, background #ffbf00 (solid gold, same as button), icon color --br #815f00. Title: Plus Jakarta 13sp 700. Subtitle: 11sp stone.
Step numbers
Gold circles (--gold #ffbf00), 16dp, deep brown text (--dp), Plus Jakarta 9sp 700. Step text: 11sp, bold keywords in --dp color.
"Allow & continue" button
Full-width pill. Background: --gold #ffbf00. Text: --dp #563e00. Font: Plus Jakarta 15sp 700. Shield-check icon left. No gradient, no shadow — flat solid gold. DO NOT change this button design.
"Why is this needed?"
Plain text link. --st #6b7280. 12sp 500. No underline, no border, no background. Just tappable text. Opens a bottom sheet with privacy/permission explainer.
Returning user empty
Real header: "Block" (Plus Jakarta 24sp 800) + "+ New" (13sp 700 brown). Shield-plus icon: 64dp, radius 18dp, background #ffbf00 (solid gold, same as button), icon --br. Title: 16sp 700. Subtitle: 12sp stone. "Create blocker" button: auto-width pill, gold, Plus Jakarta 14sp 700.