The brief from the Department of Passports came in short: citizens should be able to apply for an e-passport, check where their application stands, and pay the fee, from a phone, without an account and without a queue at a counter. Simple to say. Harder to earn, because the thing you're shipping carries a government emblem, and nobody forgives a broken payment screen on a document their travel depends on.

Three jobs, no login
I kept the scope to exactly three things, because every extra feature is an extra way to be wrong on something people can't just skip and retry later. Applying opens the official online portal in an in-app WebView instead of rebuilding that form myself β the form changes when the ministry changes it, and duplicating it would mean a second thing to keep correct forever. Checking status takes a reference number, or a scan of the barcode on the paper receipt, for the people who'd rather point a camera than retype twelve characters correctly. Paying generates a voucher β QR code, amount, reference, appointment details β and tells you in plain language to open whatever bank or wallet app you already have and scan it. No payment SDK, no new thing to trust. It rides on the QR payment rails people already use at a tea shop.

Flutter, and a folder structure I actually stuck to
Built it in Flutter for both platforms from one codebase, with Clean Architecture organized feature-first rather than layer-first β I wanted `application_status` to own its whole domain, not have its bloc three folders away from its use cases:
lib/
βββ app/ # MaterialApp.router, go_router config, theme
βββ core/ # DI (get_it), network client (Dio), permissions, shared widgets
βββ features/
βββ splash/
βββ home/
βββ apply/ # WebView portal
βββ application_status/
βββ data/ # models, remote + local data sources, repo impl
βββ domain/ # entity, repository contract, use cases
βββ presentation/ # bloc, status page, scanner page
flutter_bloc for state, go_router for navigation, get_it for DI, dio for networking. None of it is interesting on its own, and that was the point. I've watched a clever architecture choice turn into the thing nobody on the team wants to touch six months later. For an app a government agency has to stand behind, I wanted the next person reading this code to recognize every piece of it immediately.
The part that took longer than it should have
The scanner sounds like an afternoon of work β drop in `mobile_scanner`, ask for camera permission, done. It wasn't, because Android and iOS don't agree on when or how they want to be asked. Android wants the manifest permission declared and a runtime prompt through `permission_handler` right before the scanner opens; iOS wants an Info.plist usage string and a Podfile `post_install` hook that, if you get it slightly wrong, fails silently in a way that only shows up on a real device, never the simulator. I lost most of a day to that silent failure before I found it. And camera access had to stay fully optional either way β scanning a barcode is a shortcut for people whose phone has a working camera, not a requirement, so someone on an older device or with the permission denied still gets the full flow by typing the number in.

What I paid more attention to than usual
Every label in the app carries both Nepali and English, not as a localization pass bolted on afterward but because that's who's actually opening it. The app asks for nothing it doesn't need β no accounts, no analytics SDK collecting more than the flow requires, no permission requested until the exact moment it's used. None of that shows up in a demo. It's the kind of restraint you only notice by its absence, and for something with a government seal on it, I'd rather it be noticed for being unremarkable than for a scanner that never asks for permission correctly.

Where the bar actually was
I didn't set out to build anything clever here, and by the end I was glad I hadn't. The task on the other end of this app is a legal travel document, and the definition of success was narrower and higher at once: fast, bilingual, and quietly reliable, every time, for someone who has exactly one reason to open the app and no patience for it to be interesting instead of correct.