We built a pet medication tracker because every other app failed us
Source: Dev.to
Last year my partner and I adopted a dog 🐶, and with him came a lot of love… and a lot of responsibility.
I first tried Google Calendar, but work meetings, birthdays, and pet reminders all got mixed together.
After testing several pet‑care apps—some over‑designed, others missing notifications—we decided to build the tool we actually needed.
ZooMinder: What It Does
- Multiple pets – Add each pet with a photo and birthdate.
- Treatments & medications – Record dosage, frequency (daily, weekly, custom intervals) and time‑specific doses.
- Vet appointments – Store clinic info, vet name, phone, and address in one place.
- Upcoming doses – Clear timeline of the next doses across all pets.
- History – Full timeline of past doses and appointments per pet.
- Local notifications – Custom “bark” sound alerts that fire even when the app is closed.
- Offline‑first – Works without internet; data syncs to the cloud when you’re back online.
Tech Stack
| Layer | Technology |
|---|---|
| Framework | Flutter (Dart 3.10+) |
| Local DB | SQLite via sqflite |
| Backend & Auth | Supabase |
| Notifications | flutter_local_notifications + custom sound |
| Navigation | go_router |
| i18n | Flutter’s intl (English + Spanish) |
| In‑app purchases | in_app_purchase |
| Sync | Custom offline‑first sync service |
Offline‑First Sync
The app stores everything locally in SQLite first. Each record carries a synced flag. When the device regains connectivity, a SyncService pushes unsynced records to Supabase and pulls remote changes, resolving conflicts with timestamps (updated_at).
// Every model carries its sync state
class Treatment {
final bool synced;
final DateTime updatedAt;
// …
}
This design lets the app run on a plane, in a rural area, or anywhere without signal—your pet’s schedule never depends on Wi‑Fi.
Notifications
Dedicated Android notification channels are created for medications and appointments, each using a custom sound. The service schedules up to three upcoming notifications per treatment and recalculates whenever treatments are added or modified.
static const _medicationChannel = AndroidNotificationChannel(
'medication_reminders_v2',
'Medication Reminders',
importance: Importance.high,
sound: _customSound,
playSound: true,
);
Lessons Learned
- Offline‑first is worth it but painful – Sync logic with conflict resolution took more time than all the UI combined, yet the UX payoff is huge.
- Notifications are a minefield – Android permission models, battery optimizations, and timezone handling make even a simple 8 am daily reminder surprisingly complex.
- Scope creep is real – What started as “track medications” grew into appointment management, dose‑history timelines, vet‑clinic storage, premium subscriptions, and multi‑language support. Learning to say “no” to features is essential.
- Family projects keep you honest – When your co‑developer is also your partner and first user, feedback is immediate and brutally honest. 😄
What’s Next 🚀
- Family plans – Share pets between household members so everyone can track medications and appointments from their own account.
- Assign responsibilities – Delegate tasks (e.g., Mom handles the morning dose, Dad the evening one) so each person knows their role.
- Vet‑oriented tools – Features that let vets share treatment plans and visit summaries directly within ZooMinder, fostering better communication with pet owners.
Availability
ZooMinder is available on the Google Play Store. If you have pets and struggle to keep track of their medications and vet visits, give it a try.
Contribute & Feedback
We’d love your feedback. If you’re a Flutter developer, feel free to chat about the offline‑first patterns we used.
Found a bug, have a suggestion, or want to become a beta tester? Send me a DM here on dev.to. As a thank‑you, I can give you full access to the premium version of the app.
![Dog picture placeholder]
#flutter #flutterdev #dart #mobiledev #android #sideproject #indiedev
Built with Flutter, Supabase, and a lot of dog hair on the keyboard.
Have a great day! 👋🐾