03 — Component Library (Shared)
- A. Feedback & states
- B. Inputs
- C. Display
- D. Navigation & actions
- E. Content & media
- F. Shared interaction patterns
- G. Golden-test coverage expectation
Every reusable client component shared across modules. Module docs 07_Component_Library.md list module-specific components and reference these by name. Flutter mapping assumes Material 3 (
flutter/material,Material3ThemeData).
A. Feedback & states
AppSkeleton
- Purpose: loading placeholder for lists/cards/text.
- Props:
variant(text|circle|rect|list|card),width,height,lines,radius. - States: shimmer loop (
m-slow), reduced-motion = static tint. - Tokens:
surfaceVariantfill,e-0. Flutter: customSkeletonwidget orshimmerpackage wrapper (prefer custom to avoid dep).
AppEmptyState
- Purpose: empty/zero-result content.
- Props:
icon,title,message,actionLabel,onAction,compact. - States: default; compact (inline list header).
- A11y: container
Semantics(label: title + message). - Tokens:
displaySmalltitle,bodyMediumonSurfaceVariant. Flutter:Column+Icon+Text+FilledButton.tonal.
AppErrorState
- Purpose: full-screen or inline fetch failure.
- Props:
code(error code from envelope),message,onRetry,details?,compact. - Rules: never expose stack/server internals; 500 → generic text + requestId hint.
- Tokens:
erroricon,titleLarge,bodyMedium. Flutter:ErrorWidget-free custom widget.
AppOfflineBanner
- Purpose: top banner when connectivity lost.
- Props:
visible,text(i18noffline.banner),onRetry. - Behaviour: auto-dismiss on connectivity restore; sticky at top,
e-1. - Tokens:
tertiaryContainerbackground. Flutter:MaterialBanneror customAnimatedContainer.
AppSnackbar / AppToast
- Purpose: transient feedback (success, error, info, undo).
- Props:
type,message,actionLabel?,onAction?,duration. - Rules: one snackbar at a time (queue),
errortype persists ≥ 4 s, success < 3 s. - Tokens: inverse surface,
error/successicon accents. Flutter:ScaffoldMessengerwrapper.
AppBanner
- Purpose: persistent inline message (e.g., "results published", "pending KYC docs").
- Props:
severity(info|success|warning|error),title,body?,dismissible. - Tokens:
*Containerrole colors. Flutter:MaterialBanner.
B. Inputs
AppTextField
- Props:
label,hint,placeholder,keyboardType,textInputAction,formatters,validator,maxLength,prefix/suffixIcon,obscure,autofillHints,errorText,helperText,enabled,autofocus. - States: empty, filled, focused, error, disabled, read-only, loading (suffix spinner).
- Validation:
validatorrun on submit + on change after first error; error clears on edit. - A11y:
label+Semantics(error)on error; error announced. - Tokens:
surfaceVariantfill,outlineborder,erroron error, focusprimary. - Flutter:
TextField/TextFormFieldwithInputDecoration,InputFormatters.
AppDropdown (single select)
- Props:
label,items[{value,label}],value,onChanged,searchable?,loading?,emptyText,errorText,disabled. - Behaviour: opens
AppBottomSheetpicker or menu; selected value shown in field. - A11y: role "combobox",
Semanticson options. - Flutter: custom field +
showModalBottomSheet, orDropdownButtonFormFieldfor ≤ 8 items.
AppSearchBar
- Props:
hint,debounceMs(default 300),onQuery,autofocus,suffix(clear),trailing(filter chips button). - Behaviour: debounced server search; clear restores list; stays on screen during search.
- A11y: label, results announced (
liveRegion). - Flutter:
SearchBar(M3) with controller + debounceTimer.
AppChips (filter/tag/input)
- Props:
label,selected,onToggle,avatar?,onDelete?(input chips). - Flutter:
FilterChip/InputChip/ActionChip.
AppDatePicker / AppTimePicker
- Props:
label,value,min/maxDate,onChanged,errorText. - Behaviour: opens M3 date/time picker dialog; locale-aware formatting;
m-basetransition. - Flutter:
showDatePicker/showTimePickerwrappers;MaterialLocalizations.
AppSwitch / AppCheckbox / AppRadio
- Standard M3 controls; props:
label,value,onChanged,disabled,subtitle?. - A11y: label semantics,
Semantics(toggled:)/checked:. - Flutter:
Switch,Checkbox,RadiowithListTilewrappers.
AppSlider
- Props:
label,min/max/divisions,value,onChanged,valueLabel. - Flutter:
Slider+SliderTheme.
C. Display
AppCard
- Props:
title?,subtitle?,trailing?,leading?,onTap?,elevation(e-0/e-1),padding,child. - Variants: flat (
e-0), outlined (outlineVariant), tonal (primaryContainer). - A11y: whole-card tap = one
Semantics(button:); inner controls excluded from card tap. - Flutter:
Card/Card.filledwithInkWell.
AppListTile
- Props:
leading,title,subtitle,trailing,onTap,dense?,selected?,badge?,menu?(trailing overflow menu). - Height ≥ 56; dense ≥ 48. Flutter:
ListTilewith customMenuAnchor.
AppAvatar
- Props:
name,photoUrl?,size(24/32/40/48/64),presence?,onTap. - Fallback: initials from name on
primaryContainer. Flutter:CircleAvatar+CachedNetworkImage-style cache.
AppBadge
- Props:
text,color(role),icon?,outlined?. Flutter:Badge/customContainer.
AppProgress
LinearProgressIndicator(paged loads) /CircularProgressIndicator(buttons, refresh).- Tokens:
primary. Reduced-motion: determinate only.
AppTabs
- Props:
tabs[{label, icon?, badge?}],controller,scrollable?,onChanged. - Flutter:
TabBar+TabBarView(keep-alive lists).
AppCharts
- Purpose: KPI visualization (dashboard, reports, fees).
- Props:
type(bar|line|donut|gauge),series,labels,emptyText,height. - Rules: show values in tooltips + data labels; a11y: provide
Semanticssummary text; provide tabular fallback when charts not meaningful. - Flutter:
fl_chart(recommended, already industry standard; verify license) — wrap in ownAppChartwidget so the lib is swappable.
AppAvatarStack, AppStatTile, AppInfoRow
- StatTile:
label, value, delta?, trend?for dashboards. - InfoRow:
label, value, icon?for detail screens.
D. Navigation & actions
AppFAB
- Props:
icon,label?(extended),onPressed,heroTag(unique per screen!). - Rules: one FAB per screen; hidden on scroll-down, shown on scroll-up (list screens).
- Flutter:
FloatingActionButton/FloatingActionButton.extended; scroll listener.
AppButton (M3)
- Variants:
filled(primary CTA),filledTonal(secondary),outlined(tertiary),text(inline),icon(icon button w/ tooltip). - Props:
label,icon?,loading?(spinner replaces icon),enabled,destructive?(error colors),fullWidth?,semanticLabel. - Height 40/48; touch target ≥ 48.
- Flutter:
FilledButton,FilledButton.tonal,OutlinedButton,TextButton,IconButtonwithTooltip.
AppDialog
- Props:
title,content,actions,dismissible(barrier),destructiveAction?. - Behaviour: barrier tap = cancel unless
dismissible=false; ESC/back closes; focus moves into dialog;m-basescale+fade. - Flutter:
showDialog+AlertDialog;FocusScope.
AppBottomSheet
- Props:
title,draggable(drag handle),showDragHandle,children,isDismissible,height(half/full). - Rules: used for pickers, actions, filters; returns value via
Navigator.pop(result). - Flutter:
showModalBottomSheetwithDragHandle(M3).
AppMenu (context menu)
- Props:
items[{label, icon?, destructive?, onTap}],semanticLabel. - Behaviour: long-press / trailing overflow opens
MenuAnchor; ESC closes; focus ring. - Flutter:
MenuAnchor+MenuItemButton.
AppTabBarTop + AppNavigationBar + AppNavigationDrawer + AppNavigationRail
- AppNavigationBar: bottom nav for phones (3–5 destinations,
AppDestinationslist). - AppNavigationDrawer: full menu (all sections) for large screens; slide-in on phone via hamburger.
- AppNavigationRail: tablet/desktop ≥ 840 dp.
- Rules: selected destination =
primaryindicator (pill); badges show unread counts. - Flutter:
NavigationBar,NavigationDrawer,NavigationRail.
E. Content & media
AppSectionHeader
- Props:
title,actionLabel?,onAction?,subtitle?. Section gaps24top. - Flutter:
RowwithtitleMedium+TextButton.
AppAttachmentTile
- Props:
name,size,type,url,onPreview,onDownload?,uploading?,onRemove?. - Preview: PDF/image viewers; files open in external viewer via share sheet.
- Flutter:
ListTile+ file-type icon map.
AppMarkdownViewer
- Purpose: homework descriptions, announcements, notices.
- Props:
data,compact?,onLinkTap. - Flutter:
flutter_markdown(wrap; sanitize links).
AppStepper
- Purpose: multi-step flows (enrollment, admission, bulk import wizard).
- Props:
steps[{title, state}],currentIndex,onStepTap?. - Flutter: custom horizontal stepper; vertical on desktop.
AppQRScanner
- Purpose: biometric check-in, transport pickup, library issue (forward-looking: requires backend QR code support — see assumptions).
- Props:
onScanned,pauseAfterScan,flashToggle?. - Flutter:
mobile_scannerpackage (wrap).
F. Shared interaction patterns
- Pull-to-refresh on all scrollable lists →
RefreshIndicator; triggers reload + WS resubscribe. - Infinite scroll for paginated lists → bottom
CircularProgressIndicator+m-instantfade; "end of list" footer withbodySmalltext. - Optimistic updates on mark-read, toggles, likes; rollback on error + snackbar.
- Undo pattern: destructive-ish non-soft actions (delete homework draft, remove attachment) → snackbar with UNDO within 4 s.
- Empty→loading→error state machine per screen, never blank.
G. Golden-test coverage expectation
Every shared component has a golden test at 3 sizes (phone/tablet/desktop) + dark mode.