MaviKekas
A Flutter mobile app that centralizes a team's food orders — backed by a serverless Firebase backend, with the integrity logic living on the server, not the client.
Every team has that chat. The “is anyone ordering?” thread where everyone drops what they want over WhatsApp, orders get lost between memes and unrelated messages, and someone ends up scrolling back up to reconstruct who ordered what. MaviKekas came straight out of that pain: it’s an app I built for my coworkers and me, to stop chasing orders in the chat and centralize ordering in one place.
The problem
Ordering food as a group isn’t a food problem — it’s a coordination problem. Who’s ordering today? What does each person want? Are we closed yet? When all of that lives in a chat, the information exists but it’s scattered: you have to read the whole thread to build the order, and there’s always an “oh, I wanted one too” that sneaks in right after you’ve already sent it. And beyond the daily order, there was no easy way to propose “should we all get together to eat on such-and-such day?” without spinning up another improvised poll every time.
MaviKekas’s job is to turn that mess into a single flow: everyone places their order within a time window, it consolidates itself, and out comes a message ready to paste into the chat.
What it does
Centralized ordering. Each person builds their order in the app —flavors, quantities, and cheese when it applies— instead of typing it into the chat. There’s one order per person per day, so no one accidentally orders twice, and you can save a default order so you don’t have to re-enter it every time.
A window that opens and closes on its own. Ordering is only available inside its time window. At opening time, the app pings the whole group with a push notification saying “you can order now”; at closing time, ordering shuts. Nobody has to remember to open or close anything.
The consolidated order, ready for the shop. When ordering closes, whoever’s managing sees every order in real time and, with one tap, generates a formatted text ready to copy and paste into WhatsApp — the actual message sent to the shop, grouped and clean.
Proposing days to eat together. Beyond the daily order, anyone can propose a special day to get together, and the group votes (I’m in / I’m out / Something else). The vote count keeps itself, and results are shared grouped by choice.
Push notifications. The moments that matter —ordering opened, a new proposal, a poll is ready to vote— arrive as notifications, so the app tells people instead of people having to keep checking the app.
Under the hood
MaviKekas is a Flutter app from a single codebase for Android, iOS and web, on top of a 100% serverless Firebase backend — no server of my own to keep alive. The interesting part isn’t the CRUD; it’s where each rule lives.
Integrity logic lives on the server, not the client. The app mirrors the rules for the experience (disabling buttons, showing the right state), but the hard control sits behind where a modified client can’t reach it. Firestore Security Rules revalidate every write: that the order belongs to who it claims to, that there’s exactly one per person per day (via a deterministic id), and that it lands inside the ordering window — computed in the rules themselves. A hacked client can’t sneak in an order outside the window.
Vote counting is the server’s authority. Clients are forbidden from writing the poll result. When someone votes, a Cloud Function re-reads every vote and recomputes the count on the server, which flows back to everyone over real-time streams. Nobody can inflate a vote from their phone, because the number isn’t written by the phone.
Backend automation. Scheduled Cloud Functions open and close the ordering window on time, and fire the push notifications on the key events (ordering opened, proposal created, poll ready). The client never controls what it shouldn’t.
Clean architecture with Riverpod. The code is feature-first: every module follows the same three-layer pattern —UI → provider (Riverpod) → service → Firebase— with immutable models and constructor-injected dependencies. Almost all of the UI is reactive over Firestore streams, so your own order, the consolidated view and the polls update themselves.
The stack
- App — Flutter (Dart) with Material 3, one codebase for Android, iOS and web.
- State — Riverpod (providers → services → Firestore) with dependency injection.
- Data — Cloud Firestore as the single database, with Security Rules as the real access control.
- Auth — Firebase Auth (email/password), with roles (admin/user) enforced in the rules, not just in the UI.
- Active backend — Cloud Functions (Node) to automate open/close, recompute the vote count and send push.
- Notifications — Firebase Cloud Messaging (FCM), on web and mobile.
Worth noting
MaviKekas is a personal project, not a commercial product — and that’s exactly the point. It came out of a real, everyday problem my team had, and it solved that problem. But as a piece of engineering it demonstrates the things that matter: cross-platform Flutter end to end, a well-set-up serverless Firebase architecture, and the decision —the one that actually matters— to put the integrity logic on the server (Security Rules and Cloud Functions) instead of trusting the client.
It’s product thinking that starts from a real pain: spotting a small, everyday problem and, instead of putting up with it, building something that makes it go away.
Coffee & talk?
Like what you just read? I build products like this end to end — and I'm always up for a conversation. Let's talk about yours, or just trade notes over coffee.