Cross-platform digital gifting app with scratch-to-reveal and on-device image compositing
A Flutter app where a sender wraps a photo in a decorative digital wrapper, draws on it, and sends it as a link — the recipient scratches the wrapper away on their screen, with a paper-tearing sound, to reveal what is underneath. Delivered as a single codebase with a full in-app purchase and subscription implementation.
Digital greetings lost the one thing that makes a physical gift feel like a gift: the moment of unwrapping. A photo sent in a message arrives already open, and the recipient sees the content before they have any sense of anticipation.
The scratch-to-reveal interaction had to feel physical or the product had no reason to exist — a laggy gesture, an arbitrary completion threshold, or sound out of step with the finger, and the whole premise collapses. Everything else in the app is scaffolding around a few seconds of interaction.
A wrapper design and a freehand drawing are two separate layers while the sender is working, and had to become one faithfully composited image at send time, at a resolution that holds up full-screen on a modern phone — and camera photos carry orientation metadata that Flutter's own pipeline does not apply consistently, so a meaningful share of gifts would otherwise arrive sideways.
A recipient is, by definition, someone the sender wants to reach, not an existing user — so the path from a link in a text message to a full-screen reveal had to work for a stranger, on a cold start, with no login wall in front of the moment the product exists to deliver. And a consumer greetings app has unforgiving retention economics, so monetisation needed both impulse purchase of individual designs and recurring subscription, verified server-side rather than trusted from the device.
Built as a single Flutter codebase for both platforms, with build flavors expressed consistently at four levels — separate entrypoints, a settings class hierarchy, native build schemes and Gradle product flavors — so a staging build could never be mistaken for production.
Shipped the scratch and draw interactions first as embedded native platform views to get the feel right quickly, then reimplemented both in pure Dart once proven, retiring the per-platform maintenance liability rather than carrying it forward indefinitely.
Composited the wrapper and drawing layers on the device: both are rasterised from the live widget tree at high pixel ratio, decoded and alpha-composited into a single image before upload, keeping the heaviest operation in the product on the sender's hardware rather than on backend infrastructure.
Read camera orientation metadata explicitly and physically rotated the image before it entered the compositing pipeline, rather than relying on the rendering layer to apply it consistently across capture paths and devices.
Wrote the message-retrieval path to work with or without an authenticated session, so a recipient with no account and no prior install can still open what was sent to them, and drove deep links through a native host layer that parses an inbound universal link and forwards it to a globally held navigator reference, so a link opens the reveal screen directly rather than the home screen.
Wired the full purchase and subscription lifecycle explicitly — connection, listing, purchase and error streams, transaction finishing, restore and history — with receipts verified against the backend rather than trusted from a device-local callback, and migrated state management from singleton services to stream-based BLoC incrementally, new surfaces on the new pattern and old ones migrated as they were touched.
The reveal moment translates to a phone screen: a gesture-driven, audio-accompanied unwrapping that a digital greeting could not otherwise deliver, opened directly from a cold-start deep link rather than a home screen the recipient has to navigate away from.
Recipients open gifts without signing up, removing the friction between a link arriving and the product's core moment being experienced, and native platform-view complexity was retired rather than accumulated once the interaction was proven in Dart.
The product monetises through both one-off purchase and recurring subscription, with restore, cancellation and history handled properly and every purchase verified server-side, and a higher subscription tier turns subscribers into contributors by letting them upload and manage their own wrapping designs.
Something holding you back?
New build, rebuild, or adding AI to what you already have — I'll take a look and give you an honest perspective.