UI System
MySched's UI is built with a modular component library, a GoRouter-based navigation model, a centralized theming system, and WCAG-aligned accessibility standards.
Component Library
Components are organized by category in lib/ui/:
Buttons
| Component | File | Usage |
|---|---|---|
PrimaryButton | lib/ui/buttons/primary_button.dart | Main CTA actions |
SecondaryButton | lib/ui/buttons/secondary_button.dart | Secondary actions |
IconButton | lib/ui/buttons/icon_button.dart | Toolbar/nav actions |
TextButton | lib/ui/buttons/text_button.dart | Inline/tertiary actions |
Inputs
| Component | File | Usage |
|---|---|---|
StyledTextField | lib/ui/inputs/styled_text_field.dart | Text entry with validation |
TimePickerField | lib/ui/inputs/time_picker_field.dart | Class start/end time selection |
ColorPicker | lib/ui/inputs/color_picker.dart | Class color assignment |
DaySelector | lib/ui/inputs/day_selector.dart | Day-of-week multi-select |
Layout
| Component | File | Usage |
|---|---|---|
AppScaffold | lib/ui/layout/app_scaffold.dart | Standard screen wrapper |
SectionHeader | lib/ui/layout/section_header.dart | List section dividers |
CardContainer | lib/ui/layout/card_container.dart | Elevated content cards |
EmptyState | lib/ui/layout/empty_state.dart | Zero-data placeholder |
Feedback
| Component | File | Usage |
|---|---|---|
LoadingOverlay | lib/ui/feedback/loading_overlay.dart | Full-screen loading |
SnackbarMessage | lib/ui/feedback/snackbar_message.dart | Transient notifications |
ErrorBanner | lib/ui/feedback/error_banner.dart | Persistent error display |
SuccessAnimation | lib/ui/feedback/success_animation.dart | Completion confirmation |
Modals and Sheets
| Component | File | Usage |
|---|---|---|
ConfirmDialog | lib/ui/modals/confirm_dialog.dart | Destructive action confirmation |
BottomSheet | lib/ui/modals/bottom_sheet.dart | Contextual options |
ClassDetailSheet | lib/ui/modals/class_detail_sheet.dart | Class info/edit view |
Feature-Specific
| Component | File | Usage |
|---|---|---|
ScheduleGrid | lib/screens/schedule/schedule_grid.dart | Weekly schedule view |
ClassCard | lib/screens/schedule/class_card.dart | Individual class display |
ReminderTile | lib/screens/reminders/reminder_tile.dart | Reminder list item |
ScanPreview | lib/screens/scan/scan_preview.dart | OCR camera overlay |
Navigation Model
MySched uses GoRouter with 4 primary tabs and auth-guarded routes:
┌─────────────────────────────────────────────┐
│ App Shell │
│ ┌─────────┬──────────┬──────────┬────────┐ │
│ │Dashboard│Schedules │Reminders │Settings│ │
│ └────┬────┴────┬─────┴────┬─────┴───┬────┘ │
│ │ │ │ │ │
│ Overview Schedule Reminder Profile │
│ Feed Grid List Theme │
│ Detail Detail About │
│ Edit Edit Devices │
└─────────────────────────────────────────────┘Route Configuration
Dart
final router = GoRouter(
initialLocation: '/dashboard',
redirect: _authGuard,
routes: [
// Auth routes (no shell)
GoRoute(path: '/auth/sign-in', builder: (_, __) => SignInScreen()),
GoRoute(path: '/auth/sign-up', builder: (_, __) => SignUpScreen()),
// Main app (with bottom nav shell)
ShellRoute(
builder: (_, __, child) => AppShell(child: child),
routes: [
GoRoute(
path: '/dashboard',
builder: (_, __) => DashboardScreen(),
),
GoRoute(
path: '/schedules',
builder: (_, __) => ScheduleListScreen(),
routes: [
GoRoute(
path: ':id',
builder: (_, state) => ScheduleDetailScreen(
id: state.pathParameters['id']!,
),
),
],
),
GoRoute(
path: '/reminders',
builder: (_, __) => ReminderListScreen(),
),
GoRoute(
path: '/settings',
builder: (_, __) => SettingsScreen(),
),
],
),
],
);Deep Linking
MySched registers the mysched:// URL scheme for deep linking:
| URL | Destination |
|---|---|
mysched://dashboard | Dashboard tab |
mysched://schedules/{id} | Specific schedule detail |
mysched://reminders | Reminders tab |
mysched://settings | Settings tab |
mysched://scan | Launch OCR scanner |
Theming
Design tokens are centralized in lib/ui/theme/:
Dart
// lib/ui/theme/app_colors.dart
class AppColors {
// Primary
static const primary = Color(0xFF007AFF); // iOS blue
static const primaryLight = Color(0xFF4DA3FF);
static const primaryDark = Color(0xFF0055B3);
// Semantic
static const success = Color(0xFF34C759);
static const warning = Color(0xFFFF9500);
static const error = Color(0xFFFF3B30);
// Neutral
static const background = Color(0xFFF2F2F7);
static const surface = Color(0xFFFFFFFF);
static const textPrimary = Color(0xFF000000);
static const textSecondary = Color(0xFF8E8E93);
static const separator = Color(0xFFC6C6C8);
// Dark mode variants
static const backgroundDark = Color(0xFF000000);
static const surfaceDark = Color(0xFF1C1C1E);
static const textPrimaryDark = Color(0xFFFFFFFF);
static const textSecondaryDark = Color(0xFF8E8E93);
}
// lib/ui/theme/app_typography.dart
class AppTypography {
static const headline = TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
letterSpacing: -0.5,
);
static const title = TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
);
static const body = TextStyle(
fontSize: 17,
fontWeight: FontWeight.normal,
height: 1.4,
);
static const caption = TextStyle(
fontSize: 13,
fontWeight: FontWeight.normal,
color: AppColors.textSecondary,
);
}
// lib/ui/theme/app_spacing.dart
class AppSpacing {
static const xs = 4.0;
static const sm = 8.0;
static const md = 16.0;
static const lg = 24.0;
static const xl = 32.0;
static const xxl = 48.0;
}Theme Application
Dart
MaterialApp.router(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: AppColors.primary),
scaffoldBackgroundColor: AppColors.background,
appBarTheme: AppBarTheme(
backgroundColor: AppColors.surface,
foregroundColor: AppColors.textPrimary,
elevation: 0,
),
),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: AppColors.primary,
brightness: Brightness.dark,
),
scaffoldBackgroundColor: AppColors.backgroundDark,
),
routerConfig: router,
)Accessibility
MySched targets WCAG 2.1 AA compliance with the following standards:
Touch Targets
- Minimum size: 44×44 points (iOS) / 48×48 dp (Android)
- All interactive elements meet this minimum
- Adequate spacing between adjacent targets (minimum 8dp gap)
Color Contrast
- Text: Minimum 4.5:1 contrast ratio against background
- Large text (18sp+): Minimum 3:1 contrast ratio
- Interactive elements: Minimum 3:1 against adjacent colors
- Colors are never the sole means of conveying information
Screen Reader Support
Dart
// Semantic labels for all interactive elements
Semantics(
label: 'Delete class: CS 101',
hint: 'Double tap to delete this class',
button: true,
child: IconButton(
icon: Icon(Icons.delete),
onPressed: () => _deleteClass(classItem),
),
)
// Excluding decorative elements
Semantics(
excludeSemantics: true,
child: DecorativeIcon(Icons.star),
)Focus Management
- Logical focus order follows visual layout (top-to-bottom, left-to-right)
- Focus is moved to relevant content after navigation events
- Modal dialogs trap focus within their bounds
- Dismissing a modal returns focus to the trigger element
Supported Assistive Technologies
| Platform | Technology |
|---|---|
| iOS | VoiceOver, Switch Control, Dynamic Type |
| Android | TalkBack, Switch Access, Font scaling |