Architecture Overview
MySched is a cross-platform mobile application built with Flutter/Dart and powered by a Supabase backend. The app enables students to scan, manage, and share class schedules with offline-first capabilities and real-time sync.
Tech Stack
| Layer | Technology |
|---|---|
| Framework | Flutter 3.x (Dart) |
| Backend | Supabase (PostgreSQL, Auth, Edge Functions) |
| OCR | Google ML Kit Text Recognition |
| State Management | ChangeNotifier + Controller pattern |
| Navigation | GoRouter |
| Local Storage | SharedPreferences, SQLite (via Drift) |
| CI/CD | GitHub Actions |
| Analytics | Firebase Analytics + Crashlytics |
Project Structure
lib/
├── core/
│ ├── auth/ # Authentication pipeline (7 files)
│ ├── observability/ # Logging, analytics, crash reporting
│ ├── resilience/ # Retry logic, offline queue, connectivity
│ ├── ocr/ # Google ML Kit integration, schedule parsing
│ └── platform/ # Platform-specific abstractions
├── models/ # Data models (ClassItem, ReminderEntry, Section)
├── services/ # Service singletons (Supabase, sync, notifications)
├── screens/ # Screen widgets organized by feature
└── ui/
└── theme/ # Design tokens, colors, typographyArchitecture Principles
-
Offline-first — All data is cached locally. The sync engine handles conflict resolution and retry logic when connectivity returns.
-
Controller pattern — Each feature has a dedicated controller extending
ChangeNotifier. Controllers own business logic; widgets only handle presentation. -
Service singletons — Backend communication is abstracted through service classes (
SupabaseService,SyncService,NotificationService) initialized once at app startup. -
Security by default — Row-Level Security (RLS) on all 28 Supabase tables. Auth state changes are serialized to prevent race conditions.
-
Modular navigation — GoRouter with 4 primary tabs (Dashboard, Schedules, Reminders, Settings), deep linking via
mysched://scheme, and auth-guarded routes.
Documentation Map
| Section | What It Covers |
|---|---|
| Data Models | Core models, relationships, and database schema |
| Service Layer | State management, controllers, and offline queue |
| Supabase Backend | Tables, RLS policies, Edge Functions, API patterns |
| Authentication | OAuth flows, session management, error handling |
| UI System | Components, navigation, theming, accessibility |
| Testing & CI/CD | Test strategy, mocking, build pipeline, performance |
Remote Configuration
MySched uses RemoteConfigService (singleton) to manage feature flags and forced updates at runtime:
- Cache duration: 1 hour (
fetchTimeout: 10s,minimumFetchInterval: 1h) - Forced updates: Controlled via
min_app_version— when the installed version is below this value, a blocking update dialog is shown - Feature flags: Boolean flags for toggling features without app store releases (e.g.,
enable_ocr_v2,enable_sharing)
class RemoteConfigService {
static final RemoteConfigService _instance = RemoteConfigService._();
factory RemoteConfigService() => _instance;
final _remoteConfig = FirebaseRemoteConfig.instance;
Future<void> initialize() async {
await _remoteConfig.setConfigSettings(RemoteConfigSettings(
fetchTimeout: const Duration(seconds: 10),
minimumFetchInterval: const Duration(hours: 1),
));
await _remoteConfig.fetchAndActivate();
}
bool getBool(String key) => _remoteConfig.getBool(key);
String getString(String key) => _remoteConfig.getString(key);
}