import 'package:get/get.dart'; import 'package:miler/xpress/controllers/delivery.dart'; import 'package:miler/xpress/controllers/logcontroller.dart'; import 'package:miler/xpress/controllers/profile_controller.dart'; import 'package:miler/xpress/controllers/riderlog.dart'; /// ───────────────────────────────────────────────────────────────────────── /// THE DELIVERY LINE'S CONTROLLERS /// /// ── The problem this solves ── /// /// `main()` registers the parcel line's controllers — `RiderLogController`, /// `PickupController`, `LogController`, `ProfileController` — and the ported /// delivery screens ask for controllers with three of those *same names*. /// /// They are not the same classes. `miler/controllers/riderlog.dart` and /// `miler/xpress/controllers/riderlog.dart` both declare a `RiderLogController`, /// and GetX keys its container on the **type**, not the name. So a delivery /// screen calling `Get.find()` — resolving to the xpress /// type — finds nothing, no matter that a controller by that name is registered. /// /// That failure is not graceful. The delivery home page resolves its controller /// in a *field initialiser*: /// /// final DeliveryController _deliveryController = Get.find(); /// /// which runs while the widget is being constructed, so an unregistered /// controller throws before the first frame of the delivery dashboard and the /// rider gets a red screen rather than a degraded one. /// /// ── Why it is not simply added to `main()` ── /// /// It is called from `main()`, but only for a rider on the delivery line. A /// parcel rider must not pay for four controllers he will never look at — two of /// which (`RiderLogController`, `LogController`) start timers and location work /// on construction. Registering both lines' logging controllers at once would /// have a single rider filing two sets of duty logs against two different /// endpoints. /// /// So: one line's controllers, chosen once, at boot. /// /// ── Idempotent on purpose ── /// /// Guarded by `isRegistered` rather than assuming a clean container, because /// this is also called defensively from [riderShell] — a rider who signs out of /// a parcel account and into a delivery one switches lines without the process /// restarting, so boot is not the only moment this can be needed. /// ───────────────────────────────────────────────────────────────────────── /// Puts every delivery-line controller in the container. **Synchronous, and it /// must stay that way.** /// /// ── The bug this shape exists to prevent ── /// /// This was once a single `async` function that awaited `loadFromPrefs()` before /// registering `DeliveryController`. Called from [riderShell] without an await — /// correctly, since a widget builder cannot await — the first `await` handed /// control back to the framework, which built the delivery shell *during the /// suspension*, while three of the four controllers were still unregistered: /// /// "DeliveryController" not found. You need to call /// "Get.put(DeliveryController())" /// /// thrown from the home page's field initialiser, i.e. a red screen instead of a /// dashboard. A registration that a synchronous caller depends on cannot sit /// behind an await. /// /// So registration is synchronous and complete when it returns, and anything /// that needs IO is [warmDeliveryLine]'s job. void registerDeliveryLine() { if (!Get.isRegistered()) { Get.put(ProfileController(), permanent: true); } if (!Get.isRegistered()) { Get.put(DeliveryController(), permanent: true); } if (!Get.isRegistered()) { Get.put(RiderLogController(), permanent: true); } if (!Get.isRegistered()) { Get.put(LogController(), permanent: true); } } /// Registers the line, then does the IO that wants to be finished before the /// first frame. Awaited from `main()`; see [registerDeliveryLine] for why the /// two halves are separate. Future bootstrapDeliveryLine() async { final firstRun = !Get.isRegistered(); registerDeliveryLine(); // The rider's photo and name: the delivery home page's header draws the // avatar, so populating this late means the letter fallback flashes on every // launch. Mirrors what `main()` does for the parcel line. await Get.find().loadFromPrefs(); if (firstRun) { // Same call `main()` makes for parcel. It no-ops unless the rider is // already on duty — see the `onduty` guard inside `startLogging`. await Get.find().startLogging(); } }