Files
Client/CLAUDE.md
T

7.0 KiB
Raw Blame History

MarianumMobile Client

Flutter-App für die Schul-Community: Stundenplan, Ticker, Newsletter & Co. über das MarianumConnect-Backend, Nextcloud Talk + Files. Zwei Kontoarten: Schul-Konto (Schüler/Lehrer, Benutzername + Passwort) und Eltern-Konto (passwortlos per E-Mail-Code/App-Link, sieht die Stundenpläne der zugeordneten Kinder und erhält Elternbriefe, kein Talk/Files).

Stack

  • Flutter (Dart >= 3.8)
  • State: flutter_bloc + hydrated_bloc (persistente BLoCs pro Modul)
  • Navigation: persistent_bottom_nav_bar_v2 mit zentraler AppRoutes-Klasse als Single Entry Point
  • HTTP: dio, lokales Caching via localstore (Generic RequestCache<T>)
  • Calendar: syncfusion_flutter_calendar
  • Datum/Zeit: jiffy wird nur über die Extensions in lib/extensions/date_time.dart verwendet
  • Code-Gen: freezed, json_serializable

Ordnerstruktur

lib/
├── api/                    HTTP-Layer pro Backend (marianumconnect/, marianumcloud/, mhsl/ Legacy, demo/)
├── session/                Session-Modell (CredentialSession / GuardianSession), SessionManager, SessionLifecycle
├── access/                 UserRole, AccessRequirement (Gating von Modulen/Settings)
├── auth_link/              Eltern-Login: App-Link-Listener, Link-Parser, Geräte-Bindung
├── state/app/modules/      BLoC pro Feature-Modul (timetable, chat, chat_list, files, ...)
├── state/app/infrastructure LoadableState<T>, DataLoader, geteilte BLoC-Bausteine
├── view/                   Screens
│   ├── login/              Login-Flow
│   └── pages/              ein Verzeichnis pro Modul (timetable, files, talk, ...)
├── widget/                 Geteilte UI-Komponenten (Dialoge, Buttons, Sheets)
├── extensions/             DateTime-, Text-, TimeOfDay-Extensions
├── routing/                AppRoutes (Single Navigation Entry)
├── theming/                Light/Dark Theme
├── storage/                Freezed Settings-Modelle (HydratedBloc-persistent)
├── notification/           Firebase + flutter_local_notifications
└── utils/                  Helper (clipboard_helper, debouncer, download_manager, ...)

Konventionen

Navigation: Ausschließlich über AppRoutes.openX(context, ...). Direkte Navigator.push(...) für volle Pages sind nicht erlaubt Navigator.pop für Sheets/Dialogs bleibt am Call-Site.

Dialoge:

  • Info/Fehler: InfoDialog.show(context, body, copyable: true, title: '...') aus lib/widget/info_dialog.dart.
  • Bestätigung: ConfirmDialog(...).asDialog(context) aus lib/widget/confirm_dialog.dart. Async-Bestätigung nutzt onConfirmAsync (zeigt Spinner und Inline-Fehler über AsyncDialogAction).
  • Kein inline AlertDialog/SimpleDialog mehr.

Bottom-Sheets: Detail-Sheets gehen über showDetailsBottomSheet(context, header: ..., children: (ctx) => [...]) aus lib/widget/details_bottom_sheet.dart. Header ist optional.

Async-Actions: Statt manuelles Spinner+Try/Catch die AsyncActionButton-Familie aus lib/widget/async_action_button.dart (AsyncActionButton, AsyncTextButton, AsyncIconButton, AsyncFab, AsyncListTile, AsyncDialogAction, runWithErrorDialog). Fehler-Mapping läuft über errorBuilder oder zentral über errorToUserMessage aus lib/api/errors/error_mapper.dart.

Clipboard: Über copyToClipboard(context, text) aus lib/utils/clipboard_helper.dart. Zeigt automatisch SnackBar.

Datum/Zeit-Formatierung: Über die Extensions in lib/extensions/date_time.dart: dt.formatHm(), dt.formatDate(), dt.formatDateTime(), dt.formatDateShort(), dt.formatRelative(), start.timeRangeTo(end). Kein direktes Jiffy.parseFromDateTime(...).format(pattern: '...') im View-Code.

Settings: Pro Feature ein Freezed-Modell unter lib/storage/, persistiert via HydratedBloc.

Session: Die aktive Sitzung liegt in SessionManager().current (lib/session/). Nextcloud-Zugriffe nur über SessionManager().requireNextcloud() Eltern-Sessions haben keine Nextcloud-Identität. Abmelden ausschließlich über SessionLifecycle.signOut(). Die Keychain-Keys in SessionKeys sind eingefroren (Bestandsinstallationen, iOS-NSE).

Rollen & Zuschnitt: Views verzweigen nie auf Rollen. Module und Settings-Sections deklarieren AccessRequirements (nextcloud, guardian; AppModule.requirements, Settings._sections); Views bekommen ein Subjekt + eine Policy, die an genau einer Stelle als pure Funktion aufgelöst wird (Vorbild: TimetableSubject + TimetablePolicy.resolve, AbsenceFormPolicy). UserRole nur für Anzeige/Policy-Ableitung.

Stundenplan: Ein TimetableBloc(subject: …) für alle Fälle (eigener Plan, Fremdplan, Kind). Den globalen Bloc stellt PrimaryTimetableScope bereit und tauscht ihn beim Kindwechsel aus; page-scoped Fremdpläne nutzen ScopedTimetableBloc. Die Kinderauswahl (ChildSelectionCubit) ist modulübergreifend.

Elternbriefe: Modul parentLetters (nur Eltern-Sessions, AccessRequirement.guardian): Lehrer-Mitteilungen aus MarianumConnect mit Gelesen-Status, Kenntnisnahme/Auswahl/Unterschrift pro Kind, Thread und Anhängen. Der Posteingang (ParentLettersBloc) ist global (Modul-Badge, Push-Refresh), ein Brief ist page-scoped (ParentLetterBloc). Ob und wie geantwortet werden darf, entscheidet der Server (editable); ParentLetterFormPolicy.resolve macht daraus das Formular. Push: Connect-Direct-Push type: parent-letter, Tap-Routing allein über parentLetterId. Alle Benachrichtigungs-Taps (lokal gerendert wie FCM) werden von resolvePushTarget (lib/push/push_target.dart) aufgelöst und in NotificationTasks.openPushTarget navigiert neue Push-Ziele nur dort ergänzen.

Build / Run

flutter pub get
dart run build_runner build --delete-conflicting-outputs   # nach Änderungen an Freezed/JSON-Modellen
flutter run                                                  # Debug auf angeschlossenem Device
flutter analyze                                              # statische Analyse, muss 0 Issues melden
flutter test                                                 # Tests (siehe test/)

Backend-Integrationen

Backend Pfad Zweck
MarianumConnect (Bearer) lib/api/marianumconnect/ Auth, Stundenplan (Webuntis-Proxy), Ticker, Newsletter, Ferien, Abwesenheit, Elternbriefe, Capabilities, Push
Nextcloud (Talk + WebDAV) lib/api/marianumcloud/ Chats, Datei-Verwaltung
MHSL (Legacy) lib/api/mhsl/ nur noch Einmal-Migration der Custom Events

nextcloud-Paket ist auf einen Custom-Fork gepinnt (siehe pubspec.yaml dependency_overrides).

Tests

test/ deckt vor allem pure Funktionen ab (DateTime-Extensions, Stundenplan-Logik, Session-Codec, Policies, Eltern-Login-Controller). Beim Hinzufügen neuer pure-function-Helper bitte Test mit dazu.