Phase 1 · Getting Started with ComposeProject 2

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.

← 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

Build one screen using layouts, text, buttons, local resources and small saved state values. These projects use no animation APIs, navigation, databases or background work.

Beginner · 0.5–1 hours of building

The download is a complete reference app. Read the brief, predict the result, then implement each step in a separate working copy. Code runs in Android Studio on a device or emulator; the website displays and explains the source.

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

  1. 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.
  2. 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.
  3. Sync: Allow Gradle to download the pinned dependencies. Initial setup requires internet. The app runs offline and needs no backend, login or API key.
  4. Run: In Device Manager create an emulator with API 23 or newer, or connect a device with USB debugging. Select the app run configuration and press Run.
  5. 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.
  6. 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.
PowerShell — in the project folder
.\gradlew.bat :app:assembleDebug :app:testDebugUnitTest
macOS/Linux: chmod +x gradlew, then ./gradlew :app:assembleDebug :app:testDebugUnitTest. Unit tests run without a device; manual UI checks need a device or emulator.

Keep the toolchain consistent

The projects use the existing course’s pinned toolchain: Kotlin 2.2.21, AGP 8.13.2, Gradle 8.13, and Compose BOM 2025.12.01. This app uses basic Compose UI. Its Gradle files include no animation, navigation, database or ViewModel libraries. All build files and the Gradle wrapper are included. Open 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.

TOPIC 01

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.

TOPIC 02

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.

TOPIC 03

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

When a saved state value changes, Compose updates the content that reads it. 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.

MainActivity.kt
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")
Reference excerpt · lines 16–41. The full file, with imports, is in Complete source files.

Stop and check

The app displays the Business Card screen title and the sample contact card.

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.

MainActivity.kt

@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)
Reference excerpt · lines 24–49. The full file, with imports, is in Complete source files.

Stop and check

The identity information appears together inside one padded card.

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.

MainActivity.kt
        ) {
            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)
Reference excerpt · lines 31–56. The full file, with imports, is in Complete source files.

Stop and check

The name is prominent, and the role and introduction are easy to distinguish.

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.

MainActivity.kt

@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() } }
}
Reference excerpt · lines 44–61. The full file, with imports, is in Complete source files.

Stop and check

The helper renders a label above its value with consistent spacing.

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.

MainActivity.kt
    }
    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() } }
}
Reference excerpt · lines 37–61. The full file, with imports, is in Complete source files.

Stop and check

Email, phone, and location appear as three consistently styled sections.

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.

MainActivity.kt
@Preview(showBackground = true)
@Composable
private fun BusinessCardPreview() {
    MiniTheme { MiniScreen("Business Card") { BusinessCard() } }
}
Reference excerpt · lines 56–61. The full file, with imports, is in Complete source files.

Stop and check

Source edits appear in the preview, and every contact section remains readable and reachable.

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 hereResponsibility
MainActivity.ktThe launcher Activity installs the Compose UI with setContent.
CommonUi.ktMaterial 3 theme and reusable screen container with scrolling and system insets.
Application Kotlin filesLayouts, button callbacks and simple saved state introduced in the guide.
app/src/main/resImages, text resources, and the launch theme.
app/build.gradle.ktsSDK settings and the exact libraries used by this app.
README.md / TOOLCHAIN.mdStudent 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.

MainActivity.kt
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 thisExpected 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

Project acceptance checklist0 / 5 verified

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

  1. Submit the Android Studio project without build caches, .gradle or machine-specific local.properties.
  2. Include two screenshots demonstrating normal behavior and an edge case.
  3. Add a short README recording the device/API version, actions, observed outcomes, and any known limitation.
  4. 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?
CriterionPointsWhat to demonstrate
Core behavior40All required actions give the expected result.
Validation and lifecycle20Invalid inputs, empty states, rotation and the project's persistence rules work.
Interface and accessibility20Readable contrast, labeled inputs, meaningful image descriptions, scrollable content and usable touch targets.
Code and explanation20Small 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.

Further reading