Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Cristiang1021/ErgoKawsay/llms.txt

Use this file to discover all available pages before exploring further.

ErgoKawsay is structured as a feature-based Flutter application — each well-being module lives in its own folder under lib/features/, keeping screens, logic, and widgets co-located and easy to navigate. All application state (locale, theme, and accessibility settings) is managed through ChangeNotifier controllers exposed via the Provider package. There is no backend, no REST client, and no network dependency: all data is served from LocalDataRepository, a singleton that holds every module’s content in memory at runtime. Navigation is handled through Flutter’s MaterialApp.routes map, with all named routes defined centrally in app.dart.

Directory Structure

ErgoKawsay/
├── lib/
│   ├── main.dart              # App entry point, bootstrap
│   ├── app.dart               # Routes, theme, localization providers
│   ├── core/
│   │   ├── constants/         # AppConstants, AppAssets
│   │   ├── localization/      # tr.dart, AppLocalizations, controllers
│   │   ├── services/          # NotificationService
│   │   ├── storage/           # StorageService (SharedPreferences)
│   │   └── theme/             # AppTheme, AppPalette, AppTypography
│   ├── data/
│   │   ├── local/             # LocalDataRepository, quiz_data
│   │   └── models/            # Exercise, Disease, Emotion, Tip, …
│   ├── features/              # One folder per module screen
│   └── shared/widgets/        # Reusable UI components
├── assets/                    # Audio, video, images, branding
├── translations.yaml          # Source of truth for i18n strings
└── tools/gen_translations.py  # Generates lib/core/localization/tr.dart

Named Routes

All routes are registered in the routes map of MaterialApp inside app.dart. Navigation throughout the app uses Navigator.pushNamed with these paths:
RouteScreen ClassDescription
/SplashScreenAnimated splash; decides whether to show language selection or home
/languageLanguageSelectionScreenFirst-run language picker (Spanish / Kichwa)
/teacher-profileTeacherProfileScreenTeacher name and avatar setup
/homeHomeScreenMain module grid dashboard
/ergonomicsErgonomicsScreenInteractive ergonomics story deck and quiz
/diseasesDiseasesScreenMusculoskeletal disease reference cards
/active-breaksActiveBreaksScreenGuided 5-minute active-break sequence
/exercisesExercisesScreenBody-area exercise library
/remindersRemindersScreenLocal notification scheduler
/tipsTipsScreenRotating preventive tips
/emotionsEmotionsScreenEmotional health module with characters
/musicMusicScreenAmbient audio player
/videosVideosScreenIntegrated educational video player
/progressProgressScreenModule usage and habit tracking
/settingsSettingsScreenLanguage, theme, accessibility, and app info

State Management

ErgoKawsay uses the Provider package for all global reactive state. Three ChangeNotifier controllers are instantiated in _ErgoKawsayAppState.initState() and injected into the widget tree via custom InheritedWidget-based scopes defined in app.dart:
ControllerScope WidgetResponsibility
LocaleControllerLocaleControllerScopeTracks and persists the active Locale (es or qu); exposes setLocale()
ThemeControllerThemeControllerScopeTracks and persists ThemeMode (light / dark / system)
AccessibilityControllerAccessibilityControllerScopeManages text-scale factor and color-blind filter matrix
All three controllers read their initial state from StorageService on construction and write back to it on every change. They are merged into a single Listenable via Listenable.merge([...]) so the root MaterialApp rebuilds only once per state change, regardless of which controller fired. StorageServiceScope wraps the entire widget tree at the outermost level, making the StorageService instance available to any widget that needs to read or write persistent data without going through a controller.
The accessibility controller applies a ColorFiltered matrix over the entire app when the color-blind mode is enabled, and overrides the system textScaler via a root MediaQuery wrapper — both implemented in app.dart’s builder callback rather than in individual screens.

Data Layer

LocalDataRepository

LocalDataRepository is a singleton (accessed via LocalDataRepository.instance) that acts as the single source of truth for all in-app content. It exposes getters for:
  • Module cards — Ergonomics story deck, active-break steps
  • Diseases — List of Disease model objects with descriptions and advice
  • ExercisesExercise objects grouped by body-area category
  • Tips — Preventive Tip items displayed in rotation
  • EmotionsEmotion objects with their associated characters
  • Music tracks — Asset paths and metadata for the audio player
  • Videos — Asset paths and metadata for the video player
  • Quiz data — Question sets for the ergonomics review quiz
No network calls are ever made. All data is expressed as Dart constants or constructed at app startup from bundled asset files declared in pubspec.yaml.

StorageService

StorageService is a thin wrapper around SharedPreferences that provides typed read/write helpers for every persistent concern in the app:
Key ConstantData Stored
AppConstants.keyLanguageSelected language code (es / qu)
AppConstants.keyThemeModeTheme preference (light / dark / system)
AppConstants.keyAccessibilityAccessibility settings (text scale, color-blind flag)
AppConstants.keyProgressModule-usage progress data
AppConstants.keyRemindersReminder schedule configuration
AppConstants.keyNotificationsEnabledGlobal notifications on/off toggle
AppConstants.keyProfileNameTeacher’s display name
AppConstants.keyProfileAvatarTeacher’s selected avatar identifier
AppConstants.keyProfileCompletedWhether the profile setup flow has been completed
StorageService.init() is async and must be awaited before runApp() so all controllers can read persisted state synchronously on first build.

App Entry Point

The main() function in lib/main.dart follows a deliberate bootstrap order to keep startup fast and resilient:
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Disable runtime font fetching — app must work fully offline.
  GoogleFonts.config.allowRuntimeFetching = false;

  // Lock orientation to portrait before the first frame.
  await SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp,
    DeviceOrientation.portraitDown,
  ]);

  // Initialize SharedPreferences synchronously before runApp so
  // all controllers can read persisted state on their first build.
  final storage = await StorageService.init();

  runApp(ErgoKawsayApp(storage: storage));

  // Notification initialization is deferred so it never blocks the
  // first frame or leaves the app on a blank screen on slow devices.
  unawaited(_bootstrapNotifications(storage));
}
Key design decisions:
  • GoogleFonts.config.allowRuntimeFetching = false — Prevents the app from attempting font downloads, which would fail silently or stall on devices without connectivity.
  • StorageService.init() before runApp() — Ensures all ChangeNotifier controllers have access to persisted preferences synchronously during their first build, avoiding a flash of default state.
  • unawaited(_bootstrapNotifications(...)) — Notification scheduling is intentionally non-blocking. Failures are caught and logged via debugPrint without crashing the app.
The _bootstrapNotifications function checks both reminderSettings.notificationsActive and storage.notificationsEnabled before scheduling anything, respecting both the per-reminder toggle and the global notification permission the user may have denied at the OS level.

Build docs developers (and LLMs) love