5.0 KiB
name, description, type
| name | description | type |
|---|---|---|
| KROW Staff App Component Patterns | Established UI patterns, widget conventions, and design decisions confirmed in the KROW staff app codebase | project |
Card Pattern (standard surface)
Cards use:
UiColors.cardViewBackground(white) backgroundBorder.all(color: UiColors.border)outlineBorderRadius.circular(UiConstants.radiusBase)= 12dpEdgeInsets.all(UiConstants.space4)= 16dp padding
Do NOT use UiColors.bgSecondary as card background — that is for toggles/headers inside cards.
Section Toggle / Expand-Collapse Header
Used for collapsible sections inside cards:
- Background:
UiColors.bgSecondary - Radius:
UiConstants.radiusMd(6dp) - Height: minimum 48dp (touch target)
- Label:
UiTypography.titleUppercase3m.textSecondaryfor ALL-CAPS labels - Trailing:
UiIcons.chevronDownanimated 180° viaAnimatedRotation, 200ms - Ripple:
InkWellwithborderRadius: UiConstants.radiusMdand splashUiColors.primary.withValues(alpha: 0.06)
Shimmer Loading Pattern
Use UiShimmer wrapper + UiShimmerLine / UiShimmerBox / UiShimmerCircle primitives.
- Base color:
UiColors.muted - Highlight:
UiColors.background - For list content: 3 shimmer rows by default
- Do NOT use fixed height containers for shimmer — let content flow
Status Badge (read-only, non-interactive)
Custom Container with pill shape:
borderRadius: UiConstants.radiusFullpadding: EdgeInsets.symmetric(horizontal: space2, vertical: 2)- Label style:
UiTypography.footnote2b - Do NOT use the interactive
UiChipwidget for read-only display
Status color mapping:
- ACTIVE: bg=
tagActive, fg=textSuccess - PENDING: bg=
tagPending, fg=textWarning - INACTIVE/ENDED: bg=
tagFreeze, fg=textSecondary - ERROR: bg=
tagError, fg=textError
Inline Error Banner (inside card)
NOT a full-page error — a compact container inside the widget:
- bg:
UiColors.tagError - radius:
UiConstants.radiusMd - Icon:
UiIcons.erroraticonMd(20dp), color:UiColors.destructive - Title:
body2m.textError - Retry link:
body3r.primarywithTextDecoration.underline
Inline Empty State (inside card)
NOT UiEmptyState widget (that is full-page). Use compact inline version:
Icon(UiIcons.clock, size: iconXl=32, color: UiColors.iconDisabled)body2r.textSecondarylabelEdgeInsets.symmetric(vertical: space6)padding
AnimatedSize for Expand/Collapse
AnimatedSize(
duration: const Duration(milliseconds: 250),
curve: Curves.easeInOut,
child: isExpanded ? content : const SizedBox.shrink(),
)
Benefits Feature Structure
Legacy benefits: apps/mobile/legacy/legacy-staff-app/lib/features/profile/benefits/
V2 domain entity: apps/mobile/packages/domain/lib/src/entities/benefits/benefit.dart
V2 history entity: needs creation at packages/domain/lib/src/entities/benefits/benefit_history.dart
Benefit history is lazy-loaded per card (not with the initial overview fetch).
History state is cached in BLoC as Map<String, AsyncValue<List<BenefitHistory>>> keyed by benefitId.
Screen Page Pattern (overview pages)
Uses CustomScrollView with SliverList for header + SliverPadding wrapping SliverList.separated for content.
Bottom padding on content sliver: EdgeInsets.fromLTRB(16, 16, 16, 120) to clear bottom nav bar.
ShiftDateTimeSection / OrderScheduleSection — Shift Detail Section Pattern
Both widgets live in packages/features/staff/shifts/lib/src/presentation/widgets/:
shift_details/shift_date_time_section.dart— single date, clock-in/clock-out boxesorder_details/order_schedule_section.dart— date range, 7-day circle row, clock-in/clock-out boxes
Shared conventions (non-negotiable for section consistency):
- Outer padding:
EdgeInsets.all(UiConstants.space5)— 20dp all sides - Section title:
UiTypography.titleUppercase4b.textSecondary - Title → content gap:
UiConstants.space2(8dp) - Time boxes:
UiColors.bgThirdbackground,UiConstants.radiusBase(12dp) corners,UiConstants.space3(12dp) all padding - Time box label:
UiTypography.footnote2b.copyWith(color: UiColors.textSecondary, letterSpacing: 0.5) - Time box value:
UiTypography.title1m.copyWith(fontWeight: FontWeight.w700).textPrimary - Between time boxes:
UiConstants.space4(16dp) gap - Date → time boxes gap:
UiConstants.space6(24dp) - Time format:
DateFormat('h:mm a')— uppercase AM/PM with space
OrderScheduleSection day-of-week circles:
- 7 circles always shown (Mon–Sun ISO order) regardless of active days
- Circle size: 32×32dp (fixed, not a token)
- Active: bg=
UiColors.primary, text=UiColors.white, style=footnote2m - Inactive: bg=
UiColors.bgThird, text=UiColors.textSecondary, style=footnote2m - Shape:
UiConstants.radiusFull - Single-char labels: M T W T F S S
- Inter-circle gap:
UiConstants.space2(8dp) - Accessibility: wrap row with
Semantics(label: "Repeats on ..."), mark individual circles withExcludeSemantics - Ordering constant:
[DayOfWeek.mon, .tue, .wed, .thu, .fri, .sat, .sun]— do NOT derive from API list order