Katkat

Build a Compose Rating Card

OpenAIAndroid Engineer · Technical Screen

Last updated

Tests proficiency in modern Android development with Kotlin and Jetpack Compose, focusing on unidirectional data flow (UDF), custom stateless composables, ViewModel state management, and asynchronous submission handling.

Problem Description

Design and implement an interactive Feedback Rating Card in Android using Jetpack Compose and Kotlin. The component allows users to select a 1-to-5 star rating, enter an optional feedback comment, and submit the feedback to a backend repository.

Your solution should follow modern Android architecture patterns, specifically Unidirectional Data Flow (UDF) with an MVVM structure.

Requirements

  1. Stateless StarRatingBar Composable:

    • Accepts rating: Int (0 to 5, where 0 means unselected), maxStars: Int = 5, and an onRatingChanged: (Int) -> Unit callback.
    • Renders maxStars interactive icons (e.g., filled star for active, outlined star for inactive).
    • Clicking star k updates the rating to k.
  2. RatingCard Screen Composable:

    • Displays the StarRatingBar.
    • Displays a text field for an optional comment (maximum 280 characters). Show a character counter: "{current}/{max}".
    • Displays a "Submit" button:
      • Disabled if rating == 0 or if character count exceeds 280, or while submission is in progress.
    • Handles submission UI states:
      • Loading: Show a CircularProgressIndicator instead of/inside the submit button.
      • Success: Replace the card content with a "Thank you for your feedback!" confirmation view and a "Reset" action.
      • Error: Show an inline error banner or Snackbar message with a retry option.
  3. State Management (RatingViewModel):

    • Expose a single immutable StateFlow.
    • Handle actions: setRating(Int), updateComment(String), submit(), and reset().
    • Simulate an asynchronous submission call via a suspend function (e.g., delay(1000)).

Expected Interfaces

data class RatingUiState(
    val rating: Int = 0,
    val comment: String = "",
    val submissionStatus: SubmissionStatus = SubmissionStatus.Idle,
    val errorMessage: String? = null
) {
    val isValid: Boolean
        get() = rating in 1..5 && comment.length <= 280
}

sealed interface SubmissionStatus {
    object Idle : SubmissionStatus
    object Loading : SubmissionStatus
    object Success : SubmissionStatus
    object Error : SubmissionStatus
}

@Composable
fun StarRatingBar(
    rating: Int,
    onRatingChanged: (Int) -> Unit,
    modifier: Modifier = Modifier,
    maxStars: Int = 5
)

@Composable
fun RatingCardScreen(
    viewModel: RatingViewModel = viewModel(),
    modifier: Modifier = Modifier
)

Edge Cases to Consider

  • Rapid clicks on the submit button before the asynchronous call resolves (double submission).
  • Configuration changes (e.g., screen rotation) must preserve the user's draft rating and comment without re-triggering network requests.
  • Pasting text longer than 280 characters should either be truncated or highlight validation errors appropriately.

Hints

Hint 1

Separate stateful container composables from stateless presentation composables to keep components testable and reusable.

Hint 2

Use a Kotlin `sealed interface` for UI submission states and expose UI state via `StateFlow` collected with `collectAsStateWithLifecycle()`.

Hint 3

Prevent concurrent submissions by checking `submissionStatus !is SubmissionStatus.Loading` before launching the submission coroutine in the ViewModel.

Follow-up questions

  • How would you support half-star increments or smooth drag-to-rate gestures in the `StarRatingBar` composable?
  • How would you ensure accessibility compliance (e.g., TalkBack content descriptions and custom accessibility actions for star selection)?
  • How would you write automated UI tests for this screen using Compose UI testing APIs (`createComposeRule`)?