Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

13 - State Management (Transport Module)

Per-screen Cubit/Bloc design on top of 00-shared/06 conventions (stack: flutter_bloc + get_it; server state via dio repository; LoadState = Initial/Loading/Success/Error(ApiException)). Mermaid diagrams included.


1. Cubit map

CubitScreen(s) (05)Data
VehicleListCubit2page, limit, List<Vehicle>, PaginationMeta
VehicleDetailCubit3Vehicle, form state
VehicleFormCubit4form model, field errors, submit
DriverListCubit5page, limit, List<Driver>, meta
DriverDetailCubit6Driver
DriverFormCubit7form model, errors, submit
RouteListCubit8page, limit, List<Route>, meta
RouteDetailCubit9Route, resolved vehicle/driver refs
RouteFormCubit10form model, stops order, resource picks, submit
AssignmentCubit11/12assign sheet + student's assignment list
TransportOverviewCubit13 counts (vehicles/routes/drivers)
LiveTrackingCubit13(planned)
AttendanceCubit / FeesCubit14/15(planned)

Repositories (VehicleRepository, DriverRepository, RouteRepository, AssignmentRepository in features/transport/data/) are the only layer touching HTTP; they map envelopes to models and throw ApiException(status, message) (00-shared/06 §2, §3).

2. VehicleListCubit

stateDiagram-v2
    [*] --> Initial
    Initial --> Loading: fetch(page)
    Loading --> Success: 200 envelope
    Loading --> Error: 401/500
    Success --> Loading: nextPage/prevPage/pullToRefresh
    Success --> Error: refetch fails (keep stale)
    Error --> Loading: retry
    Success --> Success: delete OK (optimistic)
    Success --> Success: delete 409 (rollback + guard dialog)
  • Fetch: GET /transport/vehicles?page&limit (transport.controller.ts:36-40); meta drives pagination (buildPaginationMeta, transport.service.ts:75).
  • Delete flow: optimistic -> DELETE /transport/vehicles/:id (transport.controller.ts:54-58); on 409/404 restore row + emit DeleteBlocked(ApiException) / NotFound (transport.service.ts:94-101).

3. Detail + Form cubits (vehicle/driver/route share shape)

sequenceDiagram
    participant S as Screen
    participant D as DetailCubit
    participant F as FormCubit
    participant R as Repo
    participant A as API
    S->>D: load(id)
    D->>R: getById(id)
    R->>A: GET /transport/<entity>/:id
    A-->>R: doc (404 -> ApiException)
    R-->>D: Vehicle/Driver/Route
    D-->>S: Success(doc) / Error(404)
    S->>F: init(doc?)
    F->>R: create(dto) | update(id, dto)
    R->>A: POST | PATCH /transport/<entity>
    A-->>R: 409 ConflictException | 201/200 doc
    R-->>F: Success(doc) | Conflict(fieldMessage)
    F-->>S: SubmitDone / FieldError(conflict)
  • Form state fields: saved: bool, submitting: bool, conflicts: Map<String, String> (server 409 verbatim, transport.service.ts:38-45, 113-116, 177-190).
  • RouteFormCubit extras: stops: List<RouteStopDraft> re-sequenced on reorder (route.schema.ts:23-24), vehicleId/driverId optional strings (create-route.dto.ts:41-49).

4. AssignmentCubit (assign sheet + student assignments)

stateDiagram-v2
    [*] --> Idle
    Idle --> Loading: loadStudentAssignments(studentId)
    Loading --> Loaded(list): GET /transport/assignments/:studentId
    Loading --> Error: 401/500
    Loaded --> Submitting: submitAssign(dto)
    Submitting --> Loaded: 201 (append, refresh)
    Submitting --> Conflict: 409 duplicate
    Conflict --> Submitting: change route/student, retry
    Loaded --> Submitting: removeAssignment(id)
    Submitting --> Loaded: 200 (optimistic remove)
    Submitting --> Error: 404 (refetch)
  • Submit: POST /transport/assign body from AssignRouteDto (assign-route.dto.ts:4-26; transport.controller.ts:120-124).
  • 409 -> Conflict with message "Student already assigned to this route." (transport.service.ts:257) + "View existing" action -> student assignment screen (05 §10).
  • Remove: DELETE /transport/assignments/:id (transport.controller.ts:132-136).

5. TransportOverviewCubit

Three independent fetches (limit=1 for counts); per-tile LoadState (06 §1) so one failure never blanks the hub. Data: meta.totalItems (transport.service.ts:75, 146, 215). Refetch all on pull-to-refresh.

6. Cross-cutting

  • Cache: entity lists cached in memory (Hive optional) per tenant; detail screens read cache-first then refresh (offline tolerance, 00-shared/10 §2).
  • Ref resolution (route detail): vehicle/driver ids are raw ObjectIds (route.schema.ts:26-30); RouteDetailCubit resolves via cached lists or GET /transport/vehicles|drivers/:id in parallel; unresolved -> null.
  • Events as hints: VehicleCreated / RouteCreated / StudentRouteAssigned (transport.service.ts:47-57, 121-128, 267-278) are server-side signals; client refresh is response-driven, not event-driven.
  • Permission gating: cubits expose canCreate/canUpdate/canDelete from RBAC (permissions.constants.ts:62-74); UI hides actions accordingly.
  • Planned cubits: LiveTracking/Attendance/Fees (planned) per IMPLEMENTATION_PLAN.md:229 - no data contract yet.