MySched

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

ComponentFileUsage
PrimaryButtonlib/ui/buttons/primary_button.dartMain CTA actions
SecondaryButtonlib/ui/buttons/secondary_button.dartSecondary actions
IconButtonlib/ui/buttons/icon_button.dartToolbar/nav actions
TextButtonlib/ui/buttons/text_button.dartInline/tertiary actions

Inputs

ComponentFileUsage
StyledTextFieldlib/ui/inputs/styled_text_field.dartText entry with validation
TimePickerFieldlib/ui/inputs/time_picker_field.dartClass start/end time selection
ColorPickerlib/ui/inputs/color_picker.dartClass color assignment
DaySelectorlib/ui/inputs/day_selector.dartDay-of-week multi-select

Layout

ComponentFileUsage
AppScaffoldlib/ui/layout/app_scaffold.dartStandard screen wrapper
SectionHeaderlib/ui/layout/section_header.dartList section dividers
CardContainerlib/ui/layout/card_container.dartElevated content cards
EmptyStatelib/ui/layout/empty_state.dartZero-data placeholder

Feedback

ComponentFileUsage
LoadingOverlaylib/ui/feedback/loading_overlay.dartFull-screen loading
SnackbarMessagelib/ui/feedback/snackbar_message.dartTransient notifications
ErrorBannerlib/ui/feedback/error_banner.dartPersistent error display
SuccessAnimationlib/ui/feedback/success_animation.dartCompletion confirmation

Modals and Sheets

ComponentFileUsage
ConfirmDialoglib/ui/modals/confirm_dialog.dartDestructive action confirmation
BottomSheetlib/ui/modals/bottom_sheet.dartContextual options
ClassDetailSheetlib/ui/modals/class_detail_sheet.dartClass info/edit view

Feature-Specific

ComponentFileUsage
ScheduleGridlib/screens/schedule/schedule_grid.dartWeekly schedule view
ClassCardlib/screens/schedule/class_card.dartIndividual class display
ReminderTilelib/screens/reminders/reminder_tile.dartReminder list item
ScanPreviewlib/screens/scan/scan_preview.dartOCR camera overlay

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:

URLDestination
mysched://dashboardDashboard tab
mysched://schedules/{id}Specific schedule detail
mysched://remindersReminders tab
mysched://settingsSettings tab
mysched://scanLaunch 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

PlatformTechnology
iOSVoiceOver, Switch Control, Dynamic Type
AndroidTalkBack, Switch Access, Font scaling