First Compose App

Use Android Studio to create the project. Do not freeze dependency versions from this document. Let the current Android Studio template create the baseline, then verify versions from official release pages before changing them.

Create

  1. Open Android Studio.
  2. Create a new project with an Empty Activity or current Compose-first template.
  3. Use Kotlin.
  4. Use Material 3 if the template offers it.
  5. Set a package name you can keep through Play release.
  6. Run on an emulator and a physical device as soon as possible.

Minimal App Shape

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            AppTheme {
                TransactionListScreen(
                    state = TransactionListUiState(),
                    onEvent = {},
                )
            }
        }
    }
}

Keep the first screen stateless:

@Composable
fun TransactionListScreen(
    state: TransactionListUiState,
    onEvent: (TransactionListEvent) -> Unit,
) {
    LazyColumn {
        items(
            items = state.transactions,
            key = { transaction -> transaction.id },
        ) { transaction ->
            Text(transaction.title)
        }
    }
}

First Test

class TransactionListScreenTest {
    @get:Rule
    val compose = createComposeRule()

    @Test
    fun showsTransactionTitle() {
        compose.setContent {
            TransactionListScreen(
                state = TransactionListUiState(
                    transactions = listOf(TransactionRow("1", "Groceries")),
                ),
                onEvent = {},
            )
        }

        compose.onNodeWithText("Groceries").assertIsDisplayed()
    }
}

First Run Evidence

Record:

Template: <Android Studio version and template>
Build: ./gradlew :app:assembleDebug
Device: <emulator or physical phone>
Smoke: app opens, screen renders, rotation does not lose visible state unexpectedly

Tip: Keep previews and tests fed by fake state. Add the real ViewModel only at the route/container boundary.