Build a Compose Rating Card
Company: OpenAI
Role: Android Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Build a **rating card** in Android using **Kotlin** and **Jetpack Compose**. The card lets a user leave feedback consisting of a star rating and a written comment, then submit it.
The core requirements:
- The card contains a **star rating** input and a **multi-line comment** text field.
- The **Submit** button is **disabled** until the user has provided **both** a rating *and* a non-empty comment.
- After a **successful submission**, the card shows a **success state** (a confirmation message / success UI).
- Jetpack Compose has **no built-in rating bar**, so implement the star component yourself.
- Manage UI state with a **`ViewModel`** (MVVM-style). Keep the Composables stateless where reasonable and hoist state up.
- Code should be **clean and readable** — understandable at a glance without lengthy explanation.
### Constraints & Assumptions
- Rating scale is **1 to 5 stars** (integer steps; half-stars are out of scope unless you choose to discuss them).
- Single screen / single card — no navigation, no persistence layer required (an in-memory or mocked submit is fine).
- "Submission" can be simulated (e.g. a `suspend` call with a short delay) — you do **not** need a real network/REST client, but your design should make swapping one in trivial.
- Target a modern Compose + Material 3 setup; you may assume a recent stable Compose BOM.
- Optimize for clarity and correct state handling over feature breadth.
```hint Where to start
Separate the three concerns: a **stateless** `StarRating` composable, a **comment** field, and a **`RatingCard`** that wires them to a `ViewModel`. Think about state hoisting — which Composables own state, and which just render values and report events upward?
```
```hint State model
Try to drive the whole card from one observable state object exposed by the `ViewModel` (e.g. a `StateFlow` of an immutable UI-state class). Then ask: should "is Submit allowed?" be a stored flag, or computed from that state? Consider what goes wrong if the button's enabled-state and the validation rule are tracked separately.
```
```hint Submission state
A loose boolean (`isLoading`) struggles to express "submitting vs. succeeded vs. failed" at once. Consider what a richer representation of the submission lifecycle buys you, and how the button label/spinner and the success UI could be driven off it. The async work belongs off the UI thread.
```
```hint Custom star bar
With no built-in widget, you're rendering your own row of star icons. The two questions to answer: given the current rating, which stars look "selected"? And when the user taps a star, what rating does that produce? Keep the component stateless and don't forget accessibility for each star.
```
### Clarifying Questions to Ask
- Is the rating a strict 1–5 integer, or do we need half-star / fractional support?
- Can a user **edit** their rating/comment after tapping a star, and can they re-rate after a successful submit (reset flow)?
- Should the comment field have a **character limit** or other validation beyond "non-empty"?
- What should happen if the simulated submit **fails** — show an error and allow retry, or is the happy path sufficient for this exercise?
- Should the rating state **survive configuration changes / process death** (e.g. `SavedStateHandle`), or is in-memory `ViewModel` state enough?
- Is there a design spec for the stars (size, color, spacing), or is reasonable Material 3 styling acceptable?
### What a Strong Answer Covers
- **Unidirectional data flow / state hoisting**: stateless leaf composables, a single source of truth in the `ViewModel`.
- **Single observable state** (`StateFlow`/`State`) with `canSubmit` *derived*, not duplicated — so the disabled-button rule and validation are guaranteed consistent.
- A **correct custom star bar**: fill logic keyed off the index, tap-to-set, and accessibility (`contentDescription`, sensible touch targets).
- **Explicit submission status** (idle/submitting/success, ideally error) driving the button and the success UI; async handled in `viewModelScope` so the UI thread isn't blocked.
- **Recomposition awareness**: stable state, no business logic in the composable, `collectAsStateWithLifecycle`/`collectAsState` to observe.
- **Readability**: small focused composables, clear naming, no overengineering — matching the "understandable at a glance" bar.
- Brief mention of **testability** (the `ViewModel` logic is unit-testable without UI) and **previews**.
### Follow-up Questions
- How would you make the rating and comment **survive process death** and configuration changes?
- How would you **unit-test** the `ViewModel` (validation gating, submit success/failure transitions), and how would you UI-test the star bar with Compose testing APIs?
- How would you extend the star bar to support **half-star** ratings or a drag/swipe gesture across the stars?
- If `submit()` hit a real network endpoint, how would you handle **loading, error, and retry**, and prevent **double-submission** on rapid taps?
- How would you make the component **reusable** across screens (theming, configurable max stars, custom icons) without leaking ViewModel coupling?
Quick Answer: This question evaluates proficiency in Android UI development with Kotlin and Jetpack Compose, emphasizing custom composable design (a stateless star-rating component), MVVM-style state management via a ViewModel, form validation, and handling asynchronous submission and success states.