← Back to Prcatice more · Phase 1
Your assignment: Business Card
Build a simple contact card with initials, a name, a role, and reusable contact sections. Focus entirely on composable functions, parameters, typography, and a readable layout.
Phase 1 · Simple Compose practice
Beginner · 0.5–1 hours of building
Before you begin
- Create and run an Empty Activity project in Android Studio.
- Read basic Kotlin functions, strings, and if expressions.
- Complete the Phase 1 lessons on composables, layouts, modifiers, and basic state.
By the end, you can…
- Build a useful static screen without mutable state.
- Group a name and role inside a Card.
- Reuse a composable by passing a label and value.
- Check wrapping and scrolling with long contact details.
Required features
- Display AM, Alex Morgan, Junior Android Developer, and a short introduction.
- Show email, phone, and location as labelled contact sections.
- Render the three sections through one reusable ContactDetail function.
- Use MaterialTheme typography and a full-width layout.
- Keep sample details readable at larger font sizes and after rotation.
Set up the project and run the reference app
- Open: Extract the ZIP. In Android Studio choose Open, then select the project folder containing
settings.gradle.kts. Open that folder rather than just a Kotlin file. - Prepare: Use Android Studio’s bundled JDK 17 or 21 in Gradle settings. In SDK Manager install Android SDK Platform 36 and any Build Tools requested during sync.
- Sync: Allow Gradle to download the pinned dependencies. Initial setup requires internet. The app runs offline and needs no backend, login or API key.
- Run: In Device Manager create an emulator with API 23 or newer, or connect a device with USB debugging. Select the
apprun configuration and press Run. - Observe: Try the normal and boundary scenarios from Test the app. Record what changes after each action. Use Logcat to investigate a crash instead of changing several files at once.
- Practice: Copy the extracted folder to a working folder. Keep the build files, manifest, resources and
CommonUi.kt. Rebuild the application files following the steps below. Attempt each task before consulting its reference.
.\gradlew.bat :app:assembleDebug :app:testDebugUnitTestKeep the toolchain consistent
TOOLCHAIN.md for setup details.Understand the topics before coding
For each topic, connect the Kotlin concept to a visible behavior. Identify the value the screen reads and the event that can change that value.
Static data needs no state
The business card shows values written in the source. Nothing changes after a tap because there are no interactive controls in this first layout exercise. Ordinary string arguments are enough; rememberSaveable would add a state owner without any changing value to own.
Reusable functions
ContactDetail receives label and value and renders the same two-line layout each time. Email, phone, and location differ only in their arguments. Changing the spacing in ContactDetail updates every section.
Readable structure
The initials and name introduce the person, the role describes their work, and the contact sections follow below. A vertical layout allows long details to wrap instead of squeezing them into a narrow fixed row.
State and screen updates
rememberSaveable can restore small values after Activity recreation, such as rotation. It does not permanently save your choices. Static content needs no mutable state.Build the app step by step
Work in this order. The code panels are focused excerpts from the complete files below. They may begin or end inside a function: use the full source browser when copying a file, including its imports. After every step, run the app and check the stated result.
Step 1. Run the business card host
Your task: Trace onCreate into setContent and place BusinessCard inside the supplied MiniTheme and MiniScreen. Run the app.
Open: app/src/main/java/com/mastercoding/miniprojects/business/MainActivity.kt
How it works: The host provides shared theme colors, screen spacing, and scrolling. The BusinessCard function can therefore focus on the content rather than activity setup.
import androidx.compose.ui.unit.dp
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { MiniTheme { MiniScreen("Business Card") { BusinessCard() } } }
}
}
@Composable
fun BusinessCard() {
Card(modifier = Modifier.fillMaxWidth()) {
Column(
modifier = Modifier.padding(24.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text("AM", style = MaterialTheme.typography.displayMedium)
Text("Alex Morgan", style = MaterialTheme.typography.headlineMedium)
Text("Junior Android Developer", style = MaterialTheme.typography.titleMedium)
Text("I build small, useful Android apps with Kotlin and Compose.")
}
}
Text("Contact details", style = MaterialTheme.typography.titleLarge)
ContactDetail(label = "Email", value = "alex@example.com")
ContactDetail(label = "Phone", value = "+1 202 555 0142")
ContactDetail(label = "Location", value = "Beirut, Lebanon")Stop and check
Step 2. Create the identity card
Your task: Inside BusinessCard, add a full-width Card and a Column with 24.dp of padding and 12.dp between children.
Open: app/src/main/java/com/mastercoding/miniprojects/business/MainActivity.kt
How it works: Padding separates the text from the card edge. A Column expresses the reading order directly: initials, name, role, and introduction.
@Composable
fun BusinessCard() {
Card(modifier = Modifier.fillMaxWidth()) {
Column(
modifier = Modifier.padding(24.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text("AM", style = MaterialTheme.typography.displayMedium)
Text("Alex Morgan", style = MaterialTheme.typography.headlineMedium)
Text("Junior Android Developer", style = MaterialTheme.typography.titleMedium)
Text("I build small, useful Android apps with Kotlin and Compose.")
}
}
Text("Contact details", style = MaterialTheme.typography.titleLarge)
ContactDetail(label = "Email", value = "alex@example.com")
ContactDetail(label = "Phone", value = "+1 202 555 0142")
ContactDetail(label = "Location", value = "Beirut, Lebanon")
Text("Sample contact details for layout practice. Change them in the source to make your own card.")
}
@Composable
private fun ContactDetail(label: String, value: String) {
Column(
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)Stop and check
Step 3. Choose typography for the name and role
Your task: Display AM with displayMedium, Alex Morgan with headlineMedium, and the role with titleMedium.
Open: app/src/main/java/com/mastercoding/miniprojects/business/MainActivity.kt
How it works: Typography communicates the visual hierarchy. These values are normal text strings; the screen needs no saved state because the user is not editing them.
) {
Text("AM", style = MaterialTheme.typography.displayMedium)
Text("Alex Morgan", style = MaterialTheme.typography.headlineMedium)
Text("Junior Android Developer", style = MaterialTheme.typography.titleMedium)
Text("I build small, useful Android apps with Kotlin and Compose.")
}
}
Text("Contact details", style = MaterialTheme.typography.titleLarge)
ContactDetail(label = "Email", value = "alex@example.com")
ContactDetail(label = "Phone", value = "+1 202 555 0142")
ContactDetail(label = "Location", value = "Beirut, Lebanon")
Text("Sample contact details for layout practice. Change them in the source to make your own card.")
}
@Composable
private fun ContactDetail(label: String, value: String) {
Column(
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(label, style = MaterialTheme.typography.labelLarge)
Text(value, style = MaterialTheme.typography.bodyLarge)
}
}
@Preview(showBackground = true)Stop and check
Step 4. Extract a contact section
Your task: Write ContactDetail(label, value) using a full-width Column and two Text calls. Apply labelLarge to the label and bodyLarge to its value.
Open: app/src/main/java/com/mastercoding/miniprojects/business/MainActivity.kt
How it works: Parameters separate the presentation rule from the particular contact information. The helper can be reused for any short labelled detail without copying its layout code.
@Composable
private fun ContactDetail(label: String, value: String) {
Column(
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(label, style = MaterialTheme.typography.labelLarge)
Text(value, style = MaterialTheme.typography.bodyLarge)
}
}
@Preview(showBackground = true)
@Composable
private fun BusinessCardPreview() {
MiniTheme { MiniScreen("Business Card") { BusinessCard() } }
}Stop and check
Step 5. Reuse the contact section three times
Your task: Call ContactDetail for the sample email, phone, and location beneath the Contact details heading.
Open: app/src/main/java/com/mastercoding/miniprojects/business/MainActivity.kt
How it works: Every call supplies data to the same layout. Read the call and the function together to see how the label and value move through composable parameters.
}
Text("Contact details", style = MaterialTheme.typography.titleLarge)
ContactDetail(label = "Email", value = "alex@example.com")
ContactDetail(label = "Phone", value = "+1 202 555 0142")
ContactDetail(label = "Location", value = "Beirut, Lebanon")
Text("Sample contact details for layout practice. Change them in the source to make your own card.")
}
@Composable
private fun ContactDetail(label: String, value: String) {
Column(
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(label, style = MaterialTheme.typography.labelLarge)
Text(value, style = MaterialTheme.typography.bodyLarge)
}
}
@Preview(showBackground = true)
@Composable
private fun BusinessCardPreview() {
MiniTheme { MiniScreen("Business Card") { BusinessCard() } }
}Stop and check
Step 6. Preview and adapt the sample
Your task: Open BusinessCardPreview, then change the sample name and introduction in your working copy. Test a long email and larger system font on a device.
Open: app/src/main/java/com/mastercoding/miniprojects/business/MainActivity.kt
How it works: The preview runs the same composable tree as the activity. Long values should wrap and the supplied screen host should scroll instead of clipping the last section.
@Preview(showBackground = true)
@Composable
private fun BusinessCardPreview() {
MiniTheme { MiniScreen("Business Card") { BusinessCard() } }
}Stop and check
Complete source files
Every text file in the ZIP is available here, including Kotlin, resources, Gradle scripts and the student README. The binary Gradle wrapper JAR is included in the ZIP. Source excerpts and downloads are generated from these same files.
| Start here | Responsibility |
|---|---|
| MainActivity.kt | The launcher Activity installs the Compose UI with setContent. |
| CommonUi.kt | Material 3 theme and reusable screen container with scrolling and system insets. |
| Application Kotlin files | Layouts, button callbacks and simple saved state introduced in the guide. |
| app/src/main/res | Images, text resources, and the launch theme. |
| app/build.gradle.kts | SDK settings and the exact libraries used by this app. |
| README.md / TOOLCHAIN.md | Student instructions and setup, usable without the website. |
Android launcher Activity, setContent entry point and screen composition. Start tracing the app here.
17 complete text files. Copy the selected file with the code panel button, or download the full project above.
package com.mastercoding.miniprojects.business
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { MiniTheme { MiniScreen("Business Card") { BusinessCard() } } }
}
}
@Composable
fun BusinessCard() {
Card(modifier = Modifier.fillMaxWidth()) {
Column(
modifier = Modifier.padding(24.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text("AM", style = MaterialTheme.typography.displayMedium)
Text("Alex Morgan", style = MaterialTheme.typography.headlineMedium)
Text("Junior Android Developer", style = MaterialTheme.typography.titleMedium)
Text("I build small, useful Android apps with Kotlin and Compose.")
}
}
Text("Contact details", style = MaterialTheme.typography.titleLarge)
ContactDetail(label = "Email", value = "alex@example.com")
ContactDetail(label = "Phone", value = "+1 202 555 0142")
ContactDetail(label = "Location", value = "Beirut, Lebanon")
Text("Sample contact details for layout practice. Change them in the source to make your own card.")
}
@Composable
private fun ContactDetail(label: String, value: String) {
Column(
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(label, style = MaterialTheme.typography.labelLarge)
Text(value, style = MaterialTheme.typography.bodyLarge)
}
}
@Preview(showBackground = true)
@Composable
private fun BusinessCardPreview() {
MiniTheme { MiniScreen("Business Card") { BusinessCard() } }
}Test the app with concrete scenarios
Run these scenarios on your device or emulator. Write down the actual result beside the expected result. A successful Gradle build checks compilation; it does not demonstrate tapping, accessibility, keyboard behavior or rotation.
| Do this | Expected result |
|---|---|
| Launch the reference app. | AM, Alex Morgan, the role, and introduction appear in the identity card. |
| Read the three contact sections. | Email is alex@example.com, phone is +1 202 555 0142, and location is Beirut, Lebanon. |
| Compare label and value spacing across all sections. | All sections share the ContactDetail layout and typography. |
| Rotate the device. | The same static sample details are displayed with no state restoration logic required. |
| Change the email to a longer sample in your working copy and run it. | The value wraps within the screen width and does not hide the following section. |
| Use large fonts and TalkBack. | All text is readable and reachable by scrolling, and contact labels precede their values in reading order. |
Your completion checklist
Check an item only after demonstrating it. Progress is saved in this browser.
Troubleshooting: understand the cause
The contact sections have different spacing.
Cause: Each section was written as a separate copy of the layout.
Repair: Use ContactDetail for every section and adjust spacing in that one function.
A long email or introduction is clipped.
Cause: The text was placed in a fixed-height container or constrained to one line.
Repair: Allow Text to wrap naturally and keep the supplied scrollable screen host.
Submit your app and explain your decisions
- Submit the Android Studio project without build caches,
.gradleor machine-specificlocal.properties. - Include two screenshots demonstrating normal behavior and an edge case.
- Add a short README recording the device/API version, actions, observed outcomes, and any known limitation.
- Answer the questions below using function and state names from your own implementation.
- Why does this static screen need no remember or rememberSaveable?
- Which values enter ContactDetail as parameters?
- How does extracting ContactDetail reduce the work of changing all section spacing?
| Criterion | Points | What to demonstrate |
|---|---|---|
| Core behavior | 40 | All required actions give the expected result. |
| Validation and lifecycle | 20 | Invalid inputs, empty states, rotation and the project's persistence rules work. |
| Interface and accessibility | 20 | Readable contrast, labeled inputs, meaningful image descriptions, scrollable content and usable touch targets. |
| Code and explanation | 20 | Small named functions, clear state ownership and a correct explanation of one event-to-result trace. |
Extra challenges after the core works
- Replace the sample identity and contact strings with your own fictional card.
- Add a fourth ContactDetail call for a website name displayed as plain text.
Choose one extension, write its acceptance check first, then implement it without breaking the original scenarios.