← Back to selected work

CASE STUDY / 01 ANDROID + iOS

Trendy.
One experience.
Two platforms.

From a product that catches your eye to a cart that keeps up. A closer look at building a shared mobile shopping experience with Kotlin and Compose.

Product
Mobile commerce
Platforms
Android & iOS targets
Interface
Compose Multiplatform
Data & state
Firebase · Kotlin Flow

THE CHALLENGE

Make shopping feel like one continuous journey.

A shopper moves between discovery, product details and their cart. Those screens need to agree on the product, the quantity and what happens next—even when the data changes.

Trendy approaches that problem with shared Compose screens, feature-level ViewModels and repository-backed data flows. The repository includes product browsing, categories, account management, a cart, checkout and product administration.

This case study follows the catalog-to-cart slice. The decisions below describe the implementation in the repository and its engineering tradeoffs; they are not claims about production adoption or measured business results.

TOUCH THE EXPERIENCE

A small shop.
A real interaction.

Pick a product, choose a quantity and build a cart. Change your mind, remove an item, or start again.

  1. DiscoverBrowse the sample catalog or filter by category.
  2. ChooseOpen a product and select between 1 and 10 items.
  3. RefineEdit your cart and see the subtotal update.

This is a browser recreation of the shopping flow, inspired by the app’s interface. Products and prices are illustrative. The original app screenshots are below.

Browser recreation · Sample products · No sign-in or payment

BEHIND THE INTERFACE

Three decisions.
The reasoning behind the build.

COMPOSE MULTIPLATFORM

Share the experience. Keep the platform boundary.

Trendy puts its shopping screens and business logic in commonMain. The Android host and the iOS ComposeUIViewController connect to the same App entry point, while platform-specific integrations live outside the shared UI.

What this enables

One shared implementation can carry a product interaction across Android and iOS, reducing the number of separate UI paths to maintain.

The tradeoff

Sharing the UI does not remove platform work. Lifecycle behavior, native integrations and the feel of each platform still need separate verification.

See the iOS entry point ↗

FEATURE MODULES · KOIN

Give features a boundary, not a database dependency.

Catalog, product details, cart and checkout have their own feature modules. ViewModels receive repository interfaces through constructor injection; the shared Koin module binds those interfaces to concrete implementations.

What this enables

A screen can express what data it needs without owning the Firestore implementation. That boundary provides a place to substitute a data source or a test double.

The tradeoff

More modules bring more Gradle wiring and dependency decisions. The useful boundary is the responsibility of each feature—not simply the number of modules.

See the dependency bindings ↗

KOTLIN FLOW · EXPLICIT STATE

Derive the cart from the data that changes.

CartViewModel reads the customer stream, extracts unique product IDs with flatMapLatest, and combines the customer and product streams into cart rows. RequestState represents loading, success and error explicitly.

What this enables

The cart presentation follows the latest customer and product data. An empty cart produces an empty product list without starting a product lookup.

The tradeoff

Combining streams means handling partial and failed results. The current mapping omits cart entries whose product is missing; a future iteration could explain unavailable items directly to the shopper.

See the cart state pipeline ↗

FROM THE APP

The original experience.

Android screenshots included in the Trendy repository. These are captures of the app, separate from the interactive recreation above.

Trendy Android app: product discovery
Product discovery
Trendy Android app: browsing categories
Browsing categories
Trendy Android app: managing the cart
Managing the cart

WHAT I WOULD EXPLORE NEXT

Make the edges
as clear as the happy path.

The next useful iteration would focus on unavailable products, recovery from data errors, and predictable cart behavior when connectivity changes.

Those are proposed next steps, not completed features. The current implementation provides concrete boundaries—repository interfaces, ViewModels and request states—where that work can be developed and tested.

Read the Trendy repository ↗