Startup & bootstrap
Two things must be true before any real UI renders: the API client must know where the server is, and the device must be attested. Everything else initializes asynchronously behind the first frame.
runApp; the attestation gate then blocks the widget tree until the device is verified.The awaited part
main() starts by making zone errors fatal (BindingBase.debugZoneErrorsAreFatal = true) and reading AppDebugConfig from compile-time defines. In internal-testing builds the whole bootstrap runs inside runZonedGuarded with a print interceptor that mirrors every line into the in-memory log buffer; production builds skip the zone entirely, so the interceptor cannot cost anything where it isn't used.
Two initializations are genuinely blocking. CiviqServerApi.instance.init() loads the environment config asset, resolves and validates the base URL (HTTPS is mandatory in release; http://localhost is rewritten to 10.0.2.2 for the Android emulator in debug), and records the application-support directory that anchors every file cache. ReportingService.init() runs next because its first duty is time-sensitive: it must read the previous session's foreground marker before anything else writes a new one, since that marker is the only evidence of whether the last process death was a crash or a routine background reclaim (see Reporting). Only after that decision does bootstrap install the global error hooks and the lifecycle listener that maintains the marker going forward.
The provider tree
runApp mounts a MultiProvider whose creation order encodes the dependency graph: GeocodioService and DatabaseService start their async init() without being awaited; AccountService is created from the database provider and likewise initializes in the background — the app simply renders signed-out until stored tokens load. RepresentativesService hangs off the database through a ProxyProvider that never rebuilds it (the Architecture page explains why). The result is a first frame that does not wait for SQLite.
The gate
MyApp's home is AttestationGate(child: HomePage()) — the entire app sits behind it. The gate kicks off attestation in initState and renders one of three states: a "Verifying device…" spinner, the child on success, or a blocking error screen (back navigation disabled) with a retry button. Two conditions skip the ceremony entirely: offline mode, and internal-testing builds. Each step of the ceremony carries a 30-second timeout so a dead network fails visibly rather than hanging the spinner forever. Unsupported hardware is fatal only in release builds; debug and profile builds pass through so development on simulators works. The ceremonies themselves are documented on the Attestation page.
After the gate
HomePage composes the title bar, the map with its floating search field, and the representatives list, and schedules one post-frame task: if the previous session ended unexpectedly and a crash report is queued, the consent prompt appears now — after the UI is interactive, not before. From here the app is event-driven; the next interesting sequence is the representatives-by-location flow, which begins as soon as the map acquires a GPS fix.
Why so little is awaited: every service that could be initialized lazily is. The two exceptions exist because deferring them would corrupt correctness, not just delay content — the API client because every service closure captures its config at first use, and ReportingService because the crash-evidence marker is destroyed by the very act of starting a new session.