1. The “Why” — a School That Scattered Across the Country
Jawahar Navodaya Vidyalaya (JNV) Andaman gathered students from across the islands, gave them a residential education, and then — like every Navodaya — sent them out into the world. Years later, those alumni are everywhere: in metros, in the merchant navy, in government, in startups. The WhatsApp groups were overflowing and chaotic. Batch reunions were organised over scattered spreadsheets. Donations for a classmate in need were collected by passing around UPI IDs.
I wanted one place that felt like ours— a dedicated app where any verified JNV Andaman alumnus could find their batchmates, see what the association was doing, pay membership dues, contribute to causes, share milestones, and just… stay connected. That became Navyug.
2. What Navyug Actually Does
By the time it shipped, Navyug had grown into a genuinely full-featured social + civic app. That's 12 feature modules, around 80 screens and widgets, and 154 Dart files in lib/ — all on a single, consistent architecture.
The feature surface:
- Auth & onboarding — register with avatar + batch/JNV selection, email OTP verification, an admin approval gate, login, and forgot/reset password.
- Membership — join the association and pay annual dues via Razorpay; track membership status.
- Fundraisers — browse causes, create a fundraiser, donate via Razorpay, and track My donations and My fundraisers.
- Events — browse upcoming alumni events, view detail, register, and track My events.
- Community feed — posts with multiple photos, polls and hashtags; like, comment, reply; a searchable directory.
- Stories — FB/IG-style 24-hour ephemeral stories with audience targeting, reactions, a viewers list, and DM replies.
- Messaging — 1:1 and group conversations, realtime via Laravel Reverb, with offline cache, attachments and online presence.
- Profile, notifications & admin console — edit profile, notification preferences, an in-app + push notification centre, and a separate shell for moderators to approve member dossiers and manage roles.
3. The Stack — and Why I Picked It
I built the client in Flutter(3.27+ / Dart 3.6+) so one codebase could ship to Android and iOS — critical for a volunteer-run association where I'm the only developer. The backend is a Laravel API (Sanctum auth, Reverb realtime, Razorpay payments) documented as a strict contract before any client code was written.
Flutter 3.27+ / Dart 3.6+Riverpod (state)go_router (navigation)dio (networking)Laravel + Sanctum + ReverbRazorpay paymentsdrift (SQLite offline cache)Firebase Cloud MessagingA few of the choices that mattered most:
- State — Riverpod. Compile-safe providers, easy testing, and
StateNotifiers that play nicely with realtime streams. - Routing — go_router with a
StatefulNavigationShellso the bottom tabs keep independent navigation stacks, plus deep links so a story reaction push opens straight to/stories/{id}. - Networking — dio with an envelope-aware client. The API always returns
{ success, data, message, meta, links }, so a thinEnvelope/Paginatedlayer plus auth and error interceptors replaced unwrap logic scattered everywhere. - Realtime — pusher_channels_flutter.Laravel Reverb is Pusher-protocol compatible, so the official Pusher client just works… mostly (see the war stories below).
4. Architecture — Feature-First, Boringly Consistent
The thing I'm proudest of isn't any single feature — it's that every feature looks the same. Each feature folder splits into three layers:
data/— a repository that talks todioand returns typed models.application/— Riverpod controllers /StateNotifiers holding screen state and subscribing to realtime streams.presentation/— screens and feature-local widgets.
Shared, reusable building blocks live in core/widgets/ as a small design system — BondAppBar, BondButton, BondCard, BondTextField, BondEmptyState, BondErrorState, a mandala background and the brand marks. Naming them with a Bond*prefix gave me a quick mental filter for “this is a primitive, not a screen.” The lesson: consistency scales a solo project better than cleverness.
5. Design Language — “Navy + Saffron on Paper”
Navodaya's identity is navy and saffron, so the palette wrote itself:
Each major surface even gets its own gradient identity in the bottom nav — Feed (sunset), Events (lilac), Give (rose), Chats (moss), You (navy) — so the app feels colour-coded as you move through it. The whole palette lives in app_colors.dart with a layer of legacy aliases mapped onto the final tokens — an honest artifact of a mid-project rebrand from an earlier dark theme to the warm navy/saffron one. Real projects evolve.
6. The Feature I'm Proudest Of: Stories
Stories turned a utility app into something people open daily. I wrote a full backend spec before a line of client code — it became the contract between the Flutter app and Laravel. What makes Navyug Stories more than a clone:
Five Story Types
photo, video (≤15s), milestone (text-on-gradient cards like new job / married / baby), poll, and ask — a prompt inviting DM replies.
Audience Targeting
Every story is all, batch (only your batchmates), or jnv (only your campus) — perfect for a multi-batch alumni body.
True Ephemerality
24-hour expiry with a server cron (stories:prune) that soft-deletes the row and cleans up the media behind it.
Rich Engagement
Six reactions, a viewers list (with each viewer's reaction), poll voting, and quote-replies that arrive as DMs and survive the story's expiry via a JSON snapshot.
Realtime Updates
story.published / story.viewed / story.reactedevents over Reverb so the rail and the author's notifications update live.
Smart, Non-Spammy Push
A batchmate's milestone notifies you (opt-in), and only the first reaction per story per day pings the author, so it never gets noisy.
The “milestone cards for an alumni network” idea is the part I like most — alumni genuinely want to broadcast a new job or a wedding to their batch, and a 24-hour card is the perfect, low-pressure way to do it.
7. The Two Hardest Bugs (the War Stories)
Every project has the bugs that teach you the platform. Two stand out.
7.1 Reverb routing to the wrong cluster
Laravel Reverb is Pusher-compatible, so I used the official pusher_channels_flutter. But on Android, self-hosted Reverb traffic kept getting routed to Pusher Cloud's mt1 cluster instead of my own server. The cause: the upstream Android plugin silently dropped the host, wsPort and wssPort init arguments. iOS honoured them; Android didn't. The fix was to vendor a local patch of the plugin that forwards those args into PusherOptions, wired in via a dependency_overrides block in pubspec.yaml. A reminder that “Pusher compatible” has fine print.
7.2 Duplicate push notifications
When the backend moved to data-only FCM payloads, I had to render notifications myself from a background isolate using flutter_local_notifications. The trap: if a notification block was also present (during the migration window), the OS would auto-display it and my local notification would fire — a guaranteed duplicate. The background handler in main.dart guards against exactly this (if (message.notification != null) return;), and on iOS I set setForegroundNotificationPresentationOptions(alert: false) so APNs doesn't double up with the local banner.
8. Payments — the Razorpay Handshake
Both membership dues and donations use the same four-step flow, wrapped in a single razorpay_service.dart:
Crucially, the server is the source of truth— the client never marks a payment “done” on its own; activation only happens after server-side signature verification. For a money-handling community app, that trust boundary mattered more than any UI polish: I'm collecting real money from real alumni, so the client is never trusted with the truth.
9. Planning as a First-Class Artifact
This project lived and died by its plan/ folder. Before building, I wrote contracts, not just notes — a 30KB, 18-section REST contract covering the response envelope, auth, every endpoint, rate limits and error codes; a standalone Stories spec; and a set of integration docs for Reverb and FCM.
Writing the API contract first meant the Flutter data layer and the Laravel controllers could be built against the same agreed shape, and the envelope/pagination helpers in core/network/were designed once and reused everywhere. “Spec-first, even as a solo dev” sounds contrarian, but the API contract is the cheapest design tool you have.
10. What I'd Tell Someone Building Their Own Community App
Navyug started as a way to stop losing my batchmates to a dozen dead WhatsApp groups. It ended up being the most complete app I've built — and a small digital home for a school that's scattered across the country.
11. Frequently Asked Questions
What is Navyug?
Navyug is a Flutter community app built for the Navyug Alumni Association of JNV Andaman. Verified alumni use it to find batchmates, pay membership dues, donate to fundraisers, register for events, share posts and 24-hour stories, and chat in realtime — backed by a Laravel API with Razorpay payments and an admin approval console.
What is the tech stack?
Flutter (3.27+ / Dart 3.6+) with Riverpod, go_router and dio on the client; Laravel with Sanctum, Reverb and Razorpay on the backend. Offline chat is cached in SQLite via drift, and push runs on Firebase Cloud Messaging — all from one codebase to both Android and iOS.
Why build an app instead of using WhatsApp groups?
WhatsApp groups don't scale for an alumni body — dues get collected over UPI IDs, reunions live in spreadsheets, and there's no verified directory. Navyug puts verified members, payments, events, a community feed and ephemeral stories in one place the association owns, behind an admin approval gate.
Can you build a community or membership app for my organisation?
Yes — if you run an alumni association, club, society, NGO or membership body and need verified onboarding, dues and donation payments, events, a community feed, realtime chat and an admin console, I can design and ship it end to end.
Need a community or membership app built for your organisation?
Tell me what you're trying to ship and I'll come back with a scope, timeline and price.