What You Will Build

A premium subscription screen with a feature checklist, selectable plan cards with radio buttons, and a subscribe button. Plans highlight with a border when selected. This is the standard monetization pattern used by apps offering tiered subscriptions.

Why This Pattern Matters

Subscription screens directly impact revenue. The key UX patterns here -- visual plan comparison, clear pricing, and feature checklists -- are backed by conversion optimization research. Learning to build selectable card groups with state management is essential for any commercial app.

Key Concepts

  • mutableIntStateOf for tracking selected plan index.
  • Card with conditional BorderStroke for selection highlighting.
  • RadioButton synced with card selection state.
  • forEachIndexed to render plan options dynamically.

The Subscription Screen

@Composable
fun SubscriptionScreenScreen() {
    var selectedPlan by remember { mutableIntStateOf(1) }
    val plans = listOf(
        Triple("Monthly", "$9.99/mo", "Basic access"),
        Triple("Yearly", "$79.99/yr", "Save 33%"),
        Triple("Lifetime", "$199.99", "One-time")
    )

    Column(
        modifier = Modifier.fillMaxSize().padding(24.dp),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Spacer(Modifier.height(32.dp))
        Icon(Icons.Default.StarBorder, null,
            modifier = Modifier.size(64.dp),
            tint = Color(0xFFFFD700))
        Text("Go Premium",
            fontSize = 28.sp, fontWeight = FontWeight.Bold)
        Spacer(Modifier.height(24.dp))

        // Feature checklist
        listOf(
            "Unlimited access", "No ads",
            "Priority support", "Exclusive content"
        ).forEach {
            Row(
                Modifier.fillMaxWidth().padding(vertical = 4.dp)
            ) {
                Icon(Icons.Default.Check, null,
                    tint = Color(0xFF4CAF50))
                Spacer(Modifier.width(8.dp))
                Text(it)
            }
        }
        Spacer(Modifier.height(24.dp))

        // Plan cards with radio selection
        plans.forEachIndexed { idx, (name, price, desc) ->
            Card(
                Modifier
                    .fillMaxWidth()
                    .padding(vertical = 4.dp)
                    .clickable { selectedPlan = idx },
                border = if (idx == selectedPlan)
                    BorderStroke(2.dp, MaterialTheme.colorScheme.primary)
                else null
            ) {
                Row(
                    Modifier.padding(16.dp),
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    RadioButton(
                        selected = idx == selectedPlan,
                        onClick = { selectedPlan = idx }
                    )
                    Column(Modifier.weight(1f)) {
                        Text(name, fontWeight = FontWeight.Bold)
                        Text(desc, fontSize = 12.sp,
                            color = MaterialTheme.colorScheme.onSurfaceVariant)
                    }
                    Text(price,
                        fontWeight = FontWeight.Bold,
                        color = MaterialTheme.colorScheme.primary)
                }
            }
        }
        Spacer(Modifier.weight(1f))
        Button(
            onClick = { /* launch purchase flow */ },
            modifier = Modifier.fillMaxWidth().height(50.dp)
        ) { Text("Subscribe Now") }
    }
}

Tips and Pitfalls

  • Default selection: Pre-selecting the middle plan (index 1) is a common conversion tactic that anchors users to the recommended tier.
  • BorderStroke null trick: Passing null removes the border entirely rather than setting width to 0.
  • mutableIntStateOf vs mutableStateOf(0): The former is a Compose optimization that avoids boxing the Int value.
  • Billing integration: Connect the subscribe button to Google Play Billing Library's BillingClient.launchBillingFlow().

Minimum SDK: API 21+ with Compose BOM

Get New Tutorials by Email

No spam. Just clear, practical breakdowns you can apply right away.

Enjoy this tutorial?

Get new practical tech tutorials in your inbox.