MediBihar – An Online Medical Store App in Jetpack Compose PART - 1
In today’s fast-moving world, healthcare should be as fast and accessible as everything else. That’s the idea behind MediBihar — a modern, reliable, and user-friendly online medical store app built with Jetpack Compose for Android. Whether you need a strip of Paracetamol or a complete prescription refill, MediBihar delivers medicine at your fingertips.
This app allows users to:
- Search for medicines
- View detailed information
- Add items to a cart
- Modify quantity
- Checkout via a modern payment screen
📱 Features at a Glance
🔍 Search Medicine Instantly
Just type in the name of the medicine in the search bar and MediBihar instantly filters the list. No more scrolling endlessly or going store to store.
💊 Live Medicine List
A default list of common medicines is shown on the home screen. Each medicine shows its:
- Name
- Company
- Composition
- Price
- Dose
- Precautions
- Image
🛒 Smart Cart Integration
Each medicine has an Add to Cart button. If it’s already in the cart, it becomes a Go to Cart button for quick checkout. Smart, right?
➕➖ Modify Quantity
Inside the cart screen, we can increase or decrease the quantity of each medicine. The total price updates instantly, helping us keep track of costs.
💸 Clean Payment Screen
MediBihar features a modern Material You–style payment screen with options like:
- UPI
- Debit/Credit Card
- Wallet
- Simple, elegant, and efficient.
By the end of this tutorial, you'll have a working Android app following modern Android development practices.
📦 Tools & Libraries Used
- Jetpack Compose UI toolkit
- Material 3 design components
- MVVM Architecture
- StateFlow for reactive UI updates
- NavHost for screen navigation
Sync your project.implementation("io.coil-kt:coil-compose:2.6.0")
implementation ("androidx.compose.material:material-icons-extended")
implementation("androidx.navigation:navigation-compose:2.9.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.9.1")
🧠 Step 2: Define the Medicine Model
Create a Medicine.kt data class:
package com.codingbihar.medibihar
/// STEP 1: Data Model
// Medicine.kt
data class Medicine(
val id: Int,
val name: String,
val company: String,
val composition: String,
val dose: String,
val precautions: String,
val price: Double,
val imageResId: Int,
var quantity: Int = 0
)
Add medicine images to your res/drawable folder (e.g., paracetamol.png, aspirin.png, etc.).
👨⚕️ Step 3: ViewModel for State Management
package com.codingbihar.medibihar
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateMapOf
import androidx.lifecycle.ViewModel
class MediViewModel : ViewModel() {
private val _medicineList = mutableStateListOf<Medicine>()
val medicineList: List<Medicine> get() = _medicineList
private val _cart = mutableStateMapOf<Medicine, Int>()
val cart: Map<Medicine, Int> get() = _cart
init {
_medicineList.addAll(getSampleMedicines())
}
private fun getSampleMedicines(): List<Medicine> = listOf(
Medicine(
1,
"Paracetamol",
"Pharma Co.",
"500mg Paracetamol",
"1 tablet every 6 hours",
"Avoid alcohol",
20.0,
R.drawable.paracetamol
),
Medicine(
2,
"Ibuprofen",
"HealthMed",
"200mg Ibuprofen",
"1 tablet every 8 hours",
"Take with food",
25.0,
R.drawable.ibuprofen
),
Medicine(
3,
"Amoxicillin",
"ABC Pharma",
"500mg Amoxicillin",
"1 capsule every 12 hours",
"Complete full course",
40.0,
R.drawable.amoxicillin
),
Medicine(
id = 4,
name = "Cetirizine",
company = "AllergyCare",
composition = "Cetirizine 10mg",
dose = "1 tablet daily",
precautions = "May cause drowsiness",
price = 25.00,
imageResId = R.drawable.cetirizine,
quantity = 0
),
Medicine(
id = 21,
name = "Dolo 650",
company = "Micro Labs",
composition = "Paracetamol 650mg",
dose = "1 tablet every 6-8 hours",
precautions = "Do not exceed 4 tablets in 24 hours. Take with water. Avoid if allergic to paracetamol.",
price = 25.13,
imageResId = R.drawable.dolo650, // Replace with your actual drawable resource ID
quantity = 0
)
// Add more medicines and your drawable resources accordingly
)
fun addToCart(medicine: Medicine) {
_cart[medicine] = (_cart[medicine] ?: 0) + 1
}
fun updateQuantity(medicine: Medicine, qty: Int) {
if (qty <= 0) {
_cart.remove(medicine)
} else {
_cart[medicine] = qty
}
}
fun totalPrice(): Double {
return _cart.entries.sumOf { it.key.price * it.value }
}
fun clearCart() {
_cart.clear()
}
}
Use a List<Medicine> for your demo medicines.
🏠 Step 4: Home Screen
@Composable
fun HomeScreen(
viewModel: MediViewModel,
onMedicineClick: (Medicine) -> Unit,
onCartClick: () -> Unit
) {
var query by remember { mutableStateOf("") }
val filtered = viewModel.medicineList.filter {
it.name.contains(query, ignoreCase = true)
}
Column(modifier = Modifier.fillMaxSize().systemBarsPadding()) {
TextButton(onClick = {}
) {
Text("MediBihar - An online Medical Store",
style = MaterialTheme.typography.displayMedium)
}
TextField(
value = query,
onValueChange = { query = it },
label = { Text("Search Medicines") },
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
)
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(filtered) { medicine ->
val isInCart = viewModel.cart.keys.any { it.id == medicine.id }
MedicineCard(
medicine = medicine,
isInCart = isInCart,
onClick = { onMedicineClick(medicine) },
onAddToCart = { viewModel.addToCart(medicine) },
onGoToCartClick = onCartClick
)
}
}
}
}
@Composable
fun MedicineCard(
medicine: Medicine,
isInCart: Boolean,
onClick: () -> Unit,
onAddToCart: () -> Unit,
onGoToCartClick: () -> Unit
) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.clickable { onClick() },
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) {
Row(Modifier.padding(8.dp), verticalAlignment = Alignment.CenterVertically) {
Image(
painter = painterResource(id = medicine.imageResId),
contentDescription = medicine.name,
modifier = Modifier
.size(80.dp)
.padding(end = 8.dp)
)
Column(modifier = Modifier.weight(1f)) {
Text(medicine.name, style = MaterialTheme.typography.titleMedium)
Text("₹${medicine.price}", style = MaterialTheme.typography.bodyLarge)
Text(medicine.company, style = MaterialTheme.typography.bodySmall)
}
if (isInCart) {
Button(onClick = onGoToCartClick) {
Text("Go to Cart")
}
} else {
Button(onClick = onAddToCart) {
Text("Add")
}
}
}
}
}
Show only 10 by default with "Load More" logic.
🛒 Step 5: Cart Screen
package com.codingbihar.medibihar
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ArrowBackIosNew
import androidx.compose.material.icons.filled.Remove
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.Alignment
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
@Composable
fun CartScreen(
viewModel: MediViewModel,
onCheckout: () -> Unit,
onBack: () -> Unit
) {
val cartItems = viewModel.cart.entries.toList()
Column(modifier = Modifier
.fillMaxSize()
.systemBarsPadding()) {
IconButton(onClick = onBack) {
Icon(Icons.Filled.ArrowBackIosNew, contentDescription = "Back")
}
if (cartItems.isEmpty()) {
Text("Your cart is empty.", modifier = Modifier.padding(16.dp))
} else {
LazyColumn(modifier = Modifier.weight(1f)) {
items(cartItems) { (item, qty) ->
val totalItemPrice = item.price * qty
Row(
Modifier
.fillMaxWidth()
.padding(8.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
// ✅ Show medicine image
Image(painterResource(id = item.imageResId),
contentDescription = item.name,
modifier = Modifier
.size(104.dp)
.clip(RoundedCornerShape(8.dp))
.background(Color.LightGray)
)
Column {
Text(item.name)
Text("₹${item.price} × $qty = ₹$totalItemPrice", style = MaterialTheme.typography.bodySmall)
}
Row(verticalAlignment = Alignment.CenterVertically) {
IconButton(onClick = {
viewModel.updateQuantity(item, qty - 1)
}) {
Icon(Icons.Default.Remove, contentDescription = "Decrease")
}
Text(qty.toString(), modifier = Modifier.padding(horizontal = 8.dp))
IconButton(onClick = {
viewModel.updateQuantity(item, qty + 1)
}) {
Icon(Icons.Default.Add, contentDescription = "Increase")
}
}
}
}
}
Text(
"Total: ₹${"%.2f".format(viewModel.totalPrice())}",
modifier = Modifier.padding(16.dp),
style = MaterialTheme.typography.titleMedium
)
Button(
onClick = onCheckout,
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Text("Proceed to Checkout")
}
}
}
}
💳 Step 6: Payment Screen
@Composable
fun CheckoutScreen(viewModel: MediViewModel, onOrderPlaced: () -> Unit, onBack: () -> Unit) {
Column(Modifier.padding(16.dp)) {
IconButton(onClick = onBack) {
Icon(Icons.Default.Remove, contentDescription = "Back")
}
Text("Payment Screen", style = MaterialTheme.typography.headlineSmall)
Spacer(Modifier.height(16.dp))
Text("Total Payable: ₹${viewModel.totalPrice()}")
Spacer(Modifier.height(24.dp))
Button(onClick = {
viewModel.clearCart()
onOrderPlaced()
}) {
Text("Place Order & Pay")
}
}
}
Use Material 3 components and icons.
🧭 Step 7: Navigation Setup
@Composable
fun AppNav(viewModel: MediViewModel) {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "home") {
composable("home") {
HomeScreen(
viewModel = viewModel,
onMedicineClick = { medicine ->
navController.navigate("detail/${medicine.id}")
},
onCartClick = {
navController.navigate("cart")
},
)
}
composable(
"detail/{medicineId}",
arguments = listOf(navArgument("medicineId") { type = NavType.IntType })
) { backStackEntry ->
val medicineId = backStackEntry.arguments?.getInt("medicineId") ?: 0
val medicine = viewModel.medicineList.find { it.id == medicineId }
medicine?.let {
DetailScreen(
medicine = it,
onAddToCart = { viewModel.addToCart(it) },
onBack = { navController.popBackStack() }
)
}
}
composable("cart") {
CartScreen(
viewModel = viewModel,
onCheckout = { navController.navigate("checkout") },
onBack = { navController.popBackStack() }
)
}
composable("checkout") {
CheckoutScreen(
viewModel = viewModel,
onOrderPlaced = {
navController.popBackStack("home", inclusive = false)
},
onBack = { navController.popBackStack() }
)
}
}
}
Step8: MainActivity
class MainActivity : ComponentActivity() {
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
MediBiharTheme {
/*Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
}*/
val viewModel: MediViewModel = viewModel()
AppNav(viewModel)
}
}
}
}
🧪 Step 9: Testing & Improvements
✅ Try searching
✅ Add items to cart
✅ Adjust quantities
✅ Complete a payment
OUTPUT:
You can expand this by:
- Adding real-time search
- Firebase integration
- Local DB with Room
- Lottie animation on success
- Add Firebase Auth
- Push Notifications
MediBihar is not just an app — it’s a complete Jetpack Compose learning project for modern Android development. It’s simple, practical, and scalable. It shows how Jetpack Compose can build fast, responsive, and beautiful apps for real use cases.
From search to checkout, you’ve now built a complete online medicine shopping experience using 100% Kotlin Compose!
In the Next Part 2
To take it further and make it more like a real-world system, we will add:
- 👨⚕️ Doctor Prescription Upload
- 🔐 User Login / Sign-Up
- 📦 Order History / Status Tracking
- 📞 Contact or Help Screen
- 🌐 Firebase or Backend Integration