diff --git a/README.md b/README.md new file mode 100644 index 0000000..ee6b882 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# Order Tracking UI +You can create you custom shape to develop such type UIs. + +# Screenshot +![Screenshot_order1 (1)](https://github.com/KaushalVasava/ComposeUI/assets/49050597/06e0b81c-1932-4b02-86e2-3cd90d088c35) + +# Video +https://github.com/KaushalVasava/ComposeUI/assets/49050597/412e1e1f-45cb-4bad-a368-259ef343b656 diff --git a/app/src/main/java/com/kaushalvasava/app/composeui/screen/AuthenticationScreen.kt b/app/src/main/java/com/kaushalvasava/app/composeui/screen/AuthenticationScreen.kt new file mode 100644 index 0000000..eb38a20 --- /dev/null +++ b/app/src/main/java/com/kaushalvasava/app/composeui/screen/AuthenticationScreen.kt @@ -0,0 +1,394 @@ +package com.kaushalvasava.app.composeui.screen + +import android.widget.Toast +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.AnimatedVisibility +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.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.navigation.NavController +import androidx.navigation.compose.rememberNavController +import com.kaushalvasava.app.composeui.R +import com.kaushalvasava.app.composeui.ui.navigation.NavigationItem +import com.kaushalvasava.app.composeui.util.ValidUtil.isValidEmail +import com.kaushalvasava.app.composeui.util.ValidUtil.isValidName +import com.kaushalvasava.app.composeui.util.ValidUtil.isValidPasswordFormat + +@Preview(showBackground = false) +@Composable +fun AuthenticationScreen(navController: NavController = rememberNavController()) { + + var isNewUser by rememberSaveable { + mutableStateOf(true) + } + val backgroundColor = MaterialTheme.colorScheme.background + Column( + modifier = Modifier + .verticalScroll(rememberScrollState()) + .fillMaxSize() + .drawBehind { + drawRect( + Brush.linearGradient( + colors = listOf( + Color.Blue, + backgroundColor, + Color.Blue, + ) + ) + ) + } + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = 24.dp), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + if (isNewUser) { + "Don't have an account?" + } else { + "Already have an account?" + }, + style = MaterialTheme.typography.bodyMedium, + color = Color.White + ) + Spacer(modifier = Modifier.width(8.dp)) + Button( + onClick = { + isNewUser = !isNewUser + }, colors = ButtonDefaults.buttonColors( + containerColor = backgroundColor.copy(0.3f), + contentColor = Color.White + ), + shape = RoundedCornerShape(8.dp) + ) { + AnimatedContent(targetState = isNewUser, label = "") { + Text( + if (it) { + "Sign up" + } else { + "Sign in" + } + ) + } + } + } + Image( + painter = painterResource(id = R.drawable.s_2), + contentScale = ContentScale.Crop, + contentDescription = "logo", + modifier = Modifier + .size(200.dp, 150.dp) + .rotate(-20f) + .clip(RoundedCornerShape(16.dp)) + .align(Alignment.CenterHorizontally) + + ) + Text( + "Unique", + style = MaterialTheme.typography.displayLarge, + modifier = Modifier.align(Alignment.CenterHorizontally) + ) + Spacer(modifier = Modifier.height(16.dp)) + HorizontalDivider( + modifier = Modifier + .padding(horizontal = 32.dp) + .clip(RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp)), + thickness = 12.dp, color = MaterialTheme.colorScheme.background.copy(0.5f) + ) + InfoCard(modifier = Modifier, isNewUser = { isNewUser }) { + navController.navigate(NavigationItem.PRODUCTS) + } + } +} + +@OptIn(ExperimentalLayoutApi::class) +@Composable +fun InfoCard( + modifier: Modifier = Modifier, + isNewUser: () -> Boolean, + onSubmitClick: () -> Unit, +) { + val context = LocalContext.current + var email by rememberSaveable { + mutableStateOf("") + } + var name by rememberSaveable { + mutableStateOf("") + } + var isPwdVisible by rememberSaveable { + mutableStateOf(false) + } + val maxChar = 10 + var password by rememberSaveable { + mutableStateOf("") + } + + val isValid by remember { + derivedStateOf { + isValidEmail(email) && isValidPasswordFormat(password) + && (if (isNewUser()) isValidName(name) else true) + } + } + val msg = + "Password is Wrong!\n" + + "Please enter at least 1 digit, 1 upper case and lowercase letter, 1 special character, no white spaces, at least 8 character" + + Column( + modifier + .fillMaxWidth() + .clip(RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)) + .background(MaterialTheme.colorScheme.background) + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + AnimatedContent(targetState = isNewUser(), label = "") { + Text( + if (it) "Let's make life stylish" else "Welcome Back", + fontWeight = FontWeight.Bold, + fontSize = 32.sp + ) + } + Text( + "Enter your details below", + style = MaterialTheme.typography.bodyMedium, + fontWeight = FontWeight.Light + ) + AnimatedContent(targetState = isNewUser(), label = "flowRow") { + FlowRow( + Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + if (it) { + OutlinedTextField( + value = name, + onValueChange = { + name = it + }, + modifier = Modifier.weight(1f), + shape = RoundedCornerShape(12.dp), + textStyle = TextStyle(fontSize = 16.sp), + placeholder = { + Text("Enter name", color = Color.Gray) + }, + supportingText = { + if (!isValidName(name) && name.isNotEmpty()) { + Text( + modifier = Modifier.fillMaxWidth(), + text = "Name is wrong!", + color = MaterialTheme.colorScheme.error + ) + } + }, + isError = name.isNotEmpty() && !isValidName(name), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next) + ) + } + OutlinedTextField( + value = email, + onValueChange = { + email = it + }, + modifier = Modifier.weight(1f), + textStyle = TextStyle(fontSize = 16.sp), + placeholder = { + Text("Enter email", color = Color.Gray) + }, + shape = RoundedCornerShape(12.dp), + supportingText = { + if (!isValidEmail(email) && email.isNotEmpty()) { + Text( + modifier = Modifier.fillMaxWidth(), + text = "Email is wrong!", + color = MaterialTheme.colorScheme.error + ) + } + }, + isError = email.isNotEmpty() && !isValidEmail(email), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next) + ) + OutlinedTextField( + value = password, + onValueChange = { + if (it.length <= maxChar) password = it + }, + modifier = Modifier.weight(1f), + shape = RoundedCornerShape(12.dp), + visualTransformation = + if (isPwdVisible) + VisualTransformation.None + else + PasswordVisualTransformation(), + trailingIcon = { + IconButton( + onClick = { + isPwdVisible = !isPwdVisible + } + ) { + Icon( + if (isPwdVisible) + painterResource(id = R.drawable.ic_visibility_off) + else + painterResource(id = R.drawable.ic_visibility), + contentDescription = null + ) + } + }, + supportingText = { + if (!isValidPasswordFormat(password) && password.isNotEmpty()) { + Text( + modifier = Modifier.fillMaxWidth(), + text = msg, + color = MaterialTheme.colorScheme.error + ) + } + }, + textStyle = TextStyle(fontSize = 16.sp), + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, + imeAction = ImeAction.Done), + placeholder = { + Text("Enter password", color = Color.Gray) + }, + isError = password.isNotEmpty() && !isValidPasswordFormat(password), + ) + } + } + Button( + onClick = { + if (isValid) { + onSubmitClick() + } else { + Toast.makeText(context, "Please enter all valid details", Toast.LENGTH_SHORT) + .show() + } + }, + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .drawBehind { + drawRect( + Brush.linearGradient( + colors = listOf( + Color.Blue, + Color.Magenta + ) + ) + ) + }, + colors = ButtonDefaults.buttonColors( + containerColor = Color.Transparent, + contentColor = Color.White + ), + shape = RoundedCornerShape(12.dp) + ) { + Text( + if (isNewUser()) "Sign up" else "Sign in", + modifier = Modifier.padding(vertical = 8.dp) + ) + } + AnimatedVisibility(!isNewUser()) { + TextButton(onClick = { + //forgot password + }) { + Text("Forgot your password?") + } + } + Row( + Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceAround, + verticalAlignment = Alignment.CenterVertically + ) { + HorizontalDivider(Modifier.weight(1f)) + AnimatedContent(targetState = isNewUser(), label = "dg") { + Text( + if (it) { + "Or sign up with" + } else { + "Or sign in with" + }, + modifier = Modifier.weight(1f), + textAlign = TextAlign.Center + ) + } + HorizontalDivider(Modifier.weight(1f)) + } + Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { + OutlinedButton(onClick = { /*TODO*/ }, shape = RoundedCornerShape(8.dp)) { + Icon( + painter = painterResource(id = R.drawable.ic_google), + contentDescription = "google", + modifier = Modifier.size(ButtonDefaults.IconSize), + tint = Color.Unspecified + ) + Spacer(modifier = Modifier.width(16.dp)) + Text(text = "Google") + } + OutlinedButton(onClick = { /*TODO*/ }, shape = RoundedCornerShape(8.dp)) { + Icon( + painter = painterResource(id = R.drawable.ic_facebook), + contentDescription = "Facebook", + modifier = Modifier.size(ButtonDefaults.IconSize), + tint = Color.Unspecified + ) + Spacer(modifier = Modifier.width(16.dp)) + Text(text = "Facebook") + } + } + Spacer(Modifier.weight(1f)) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kaushalvasava/app/composeui/screen/OrderScreen.kt b/app/src/main/java/com/kaushalvasava/app/composeui/screen/OrderScreen.kt new file mode 100644 index 0000000..8244eeb --- /dev/null +++ b/app/src/main/java/com/kaushalvasava/app/composeui/screen/OrderScreen.kt @@ -0,0 +1,112 @@ +package com.kaushalvasava.app.composeui.screen + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.ArrowBack +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import androidx.navigation.compose.rememberNavController +import com.kaushalvasava.app.composeui.R +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Preview(showBackground = false) +@Composable +fun OrderScreen(navController: NavController = rememberNavController()) { + + var isBottomSheet by remember { + mutableStateOf(false) + } + + val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + val coroutineScope = rememberCoroutineScope() + + if (isBottomSheet) { + ModalBottomSheet( + sheetState = bottomSheetState, + onDismissRequest = { + coroutineScope.launch { + isBottomSheet = false + bottomSheetState.hide() + } + }) { + OrderTrackingScreen() + } + } + Scaffold( + topBar = { + TopAppBar(title = { Text("Orders") }, navigationIcon = { + IconButton(onClick = { }) { + Icon(Icons.Rounded.ArrowBack, null) + } + }) + }, + ) { + LazyColumn(Modifier.padding(it)) { + items(8) { + OrderItem { + coroutineScope.launch { + isBottomSheet = true + bottomSheetState.show() + } +// navController.navigate(NavigationItem.ORDER_TRACKING) + } + } + } + } +} + +@Composable +fun OrderItem(onClick: () -> Unit) { + Row( + Modifier + .fillMaxWidth() + .clickable { + onClick() + } + .padding(8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Image( + painter = painterResource(id = R.drawable.s_3), + contentDescription = null, + modifier = Modifier.size(80.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) + Column { + Text("Shoes Green", style = MaterialTheme.typography.titleMedium) + Text("Items: 3", style = MaterialTheme.typography.bodyMedium) + Text("Quantity: 1", style = MaterialTheme.typography.bodyMedium) + Text("Price: Rs. 1200", style = MaterialTheme.typography.bodyMedium) + Text("Delivery Status: Pending", style = MaterialTheme.typography.bodyMedium) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kaushalvasava/app/composeui/screen/OrderTrackingScreen.kt b/app/src/main/java/com/kaushalvasava/app/composeui/screen/OrderTrackingScreen.kt new file mode 100644 index 0000000..dab8f29 --- /dev/null +++ b/app/src/main/java/com/kaushalvasava/app/composeui/screen/OrderTrackingScreen.kt @@ -0,0 +1,235 @@ +package com.kaushalvasava.app.composeui.screen + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Person +import androidx.compose.material.icons.rounded.Phone +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +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.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Rect +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Outline +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.kaushalvasava.app.composeui.R +import kotlin.math.roundToInt + +@Preview(showBackground = true) +@Composable +fun OrderTrackingScreen() { + var isDelivered by remember { + mutableStateOf(false) + } + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.padding(horizontal = 8.dp) + ) { + Row( + Modifier + .fillMaxWidth() + .padding(vertical = 8.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Image( + painter = painterResource(id = R.drawable.s_3), + contentDescription = null, + modifier = Modifier.size(60.dp) + ) + Column { + Text("Shoes S2 - Green", fontWeight = FontWeight.Bold) + Text("Quantity: 1", fontSize = 14.sp) + } + } + OrderTrackingCard { + isDelivered + } + Text( + "Related Information", + style = MaterialTheme.typography.titleLarge, + fontWeight = FontWeight.Bold + ) + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Image( + Icons.Rounded.Person, + contentDescription = "", + modifier = Modifier + .clip(CircleShape) + .background(Color.Green.copy(0.2f)) + .size(50.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) + Column { + Text("Kaushal V.") + Text("Courrier") + } + Spacer(modifier = Modifier.weight(1f)) + IconButton( + onClick = { + //do call to driver + }, + modifier = Modifier + .clip(CircleShape) + .background(Color.Green.copy(0.2f)) + ) { + Icon(Icons.Rounded.Phone, contentDescription = "call") + } + } + Button( + onClick = { + isDelivered = !isDelivered + }, + modifier = Modifier + .padding(bottom = 16.dp) + .fillMaxWidth() + .clip(RoundedCornerShape(16.dp)) + .drawBehind { + drawRect( + Brush.linearGradient( + listOf(Color.Blue, Color.Green) + ) + ) + } + .align(Alignment.CenterHorizontally), + colors = ButtonDefaults.buttonColors( + containerColor = Color.Transparent, + contentColor = Color.White + ), + shape = RoundedCornerShape(16.dp) + ) { + Text( + "Pay Now Rs. 1200", + style = MaterialTheme.typography.bodyLarge, + fontWeight = FontWeight.SemiBold + ) + } + } +} + +@Preview(showBackground = false) +@Composable +fun OrderTrackingCard(modifier: Modifier = Modifier, isDelivered: () -> Boolean = { false }) { + Card( + modifier, + colors = CardDefaults.cardColors( + containerColor = Color.Green.copy(0.2f) + ) + ) { + Column(Modifier.padding(8.dp)) { + Row(Modifier.fillMaxWidth()) { + Icon( + painter = painterResource(id = R.drawable.ic_location), + contentDescription = "null" + ) + Column(Modifier.weight(1f)) { + Text("Pickup Point", fontWeight = FontWeight.Bold, fontSize = 16.sp) + Text( + "Shop no.16 K Store, Zadeshwar ", + maxLines = 2, + overflow = TextOverflow.Ellipsis, + fontWeight = FontWeight.Light, + fontSize = 14.sp + ) + } + Column(horizontalAlignment = Alignment.End) { + Text("Order Time", fontWeight = FontWeight.Bold, fontSize = 16.sp) + Text("12 pm", fontWeight = FontWeight.Light, fontSize = 14.sp) + } + } + Box( + Modifier + .width(4.dp) + .height(80.dp) + .background( + color = if (isDelivered()) { + Color(0xFF16AB1D) + } else + Color.Gray, + shape = DottedShape(step = 15.dp) + ) + ) + Row(Modifier.fillMaxWidth()) { + Icon( + painter = painterResource(id = R.drawable.ic_location), + contentDescription = "null" + ) + Column { + Text("Delivery Point", fontWeight = FontWeight.Bold, fontSize = 16.sp) + Text("Zadeshwar no.2", fontWeight = FontWeight.Light, fontSize = 14.sp) + } + Spacer(Modifier.weight(1f)) + Column(horizontalAlignment = Alignment.End) { + Text("Delivery Time", fontWeight = FontWeight.Bold, fontSize = 16.sp) + Text("3 pm", fontWeight = FontWeight.Light, fontSize = 14.sp) + } + } + } + } +} + + +private data class DottedShape( + val step: Dp, +) : Shape { + override fun createOutline( + size: Size, + layoutDirection: LayoutDirection, + density: Density, + ) = Outline.Generic(Path().apply { + val stepPx = with(density) { step.toPx() } + val stepsCount = (size.height / stepPx).roundToInt() + val actualStep = size.height / stepsCount + val dotSize = Size(width = size.width, height = actualStep / 2) + for (i in 0 until stepsCount) { + addRect( + Rect( + offset = Offset(y = i * actualStep, x = 30f), + size = dotSize + ) + ) + } + close() + }) +} diff --git a/app/src/main/java/com/kaushalvasava/app/composeui/screen/ProductDetailScreen.kt b/app/src/main/java/com/kaushalvasava/app/composeui/screen/ProductDetailScreen.kt index f58c451..4e9d90e 100644 --- a/app/src/main/java/com/kaushalvasava/app/composeui/screen/ProductDetailScreen.kt +++ b/app/src/main/java/com/kaushalvasava/app/composeui/screen/ProductDetailScreen.kt @@ -61,6 +61,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController import androidx.navigation.compose.rememberNavController +import com.kaushalvasava.app.composeui.ui.navigation.NavigationItem import kotlinx.coroutines.delay @Preview @@ -373,7 +374,9 @@ fun ProductDetailScreen( colors = ButtonDefaults.buttonColors( containerColor = Color.Blue ), - onClick = {} + onClick = { + navController.navigate(NavigationItem.ORDERS) + } ) { Icon(Icons.Rounded.ShoppingCart, contentDescription = null, tint = Color.White) Text("Add to Cart") diff --git a/app/src/main/java/com/kaushalvasava/app/composeui/ui/navigation/AppNavHost.kt b/app/src/main/java/com/kaushalvasava/app/composeui/ui/navigation/AppNavHost.kt index 0f37c05..528e499 100644 --- a/app/src/main/java/com/kaushalvasava/app/composeui/ui/navigation/AppNavHost.kt +++ b/app/src/main/java/com/kaushalvasava/app/composeui/ui/navigation/AppNavHost.kt @@ -9,6 +9,9 @@ import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.navArgument +import com.kaushalvasava.app.composeui.screen.AuthenticationScreen +import com.kaushalvasava.app.composeui.screen.OrderScreen +import com.kaushalvasava.app.composeui.screen.OrderTrackingScreen import com.kaushalvasava.app.composeui.screen.ProductDetailScreen import com.kaushalvasava.app.composeui.screen.ProductsScreen @@ -16,7 +19,7 @@ import com.kaushalvasava.app.composeui.screen.ProductsScreen fun AppNavHost( modifier: Modifier = Modifier, navController: NavHostController, - startDestination: String = NavigationItem.PRODUCTS, + startDestination: String = NavigationItem.ORDERS, ) { NavHost( modifier = modifier, @@ -47,6 +50,9 @@ fun AppNavHost( ) } ) { + composable(NavigationItem.AUTHENTICATION) { + AuthenticationScreen(navController) + } composable(NavigationItem.PRODUCTS) { ProductsScreen(navController) } @@ -59,5 +65,11 @@ fun AppNavHost( if (productId != null) ProductDetailScreen(productId = productId, navController = navController) } + composable(NavigationItem.ORDERS) { + OrderScreen(navController) + } + composable(NavigationItem.ORDER_TRACKING) { + OrderTrackingScreen() + } } } \ No newline at end of file diff --git a/app/src/main/java/com/kaushalvasava/app/composeui/ui/navigation/NavigationItem.kt b/app/src/main/java/com/kaushalvasava/app/composeui/ui/navigation/NavigationItem.kt index 6f75455..302f233 100644 --- a/app/src/main/java/com/kaushalvasava/app/composeui/ui/navigation/NavigationItem.kt +++ b/app/src/main/java/com/kaushalvasava/app/composeui/ui/navigation/NavigationItem.kt @@ -3,4 +3,7 @@ package com.kaushalvasava.app.composeui.ui.navigation object NavigationItem { const val PRODUCTS = "products" const val PRODUCT_DETAILS = "product_details" + const val AUTHENTICATION = "authentication" + const val ORDERS = "orders" + const val ORDER_TRACKING = "order_tracking" } \ No newline at end of file diff --git a/app/src/main/java/com/kaushalvasava/app/composeui/util/ValidUtil.kt b/app/src/main/java/com/kaushalvasava/app/composeui/util/ValidUtil.kt new file mode 100644 index 0000000..135e703 --- /dev/null +++ b/app/src/main/java/com/kaushalvasava/app/composeui/util/ValidUtil.kt @@ -0,0 +1,34 @@ +package com.kaushalvasava.app.composeui.util + +import android.util.Patterns +import java.util.regex.Pattern + +object ValidUtil { + + fun isValidEmail(email: String): Boolean { + val pattern: Pattern = Patterns.EMAIL_ADDRESS + return pattern.matcher(email).matches() + } + + fun isValidName(name: String): Boolean { + val pattern: Pattern = + Pattern.compile("^([a-zA-Z]{2,}\\s[a-zA-Z]+'?-?[a-zA-Z]{2,}\\s?([a-zA-Z]+)?)") + return pattern.matcher(name).matches() + } + + + fun isValidPasswordFormat(password: String): Boolean { + val passwordREGEX = Pattern.compile( + "^" + + "(?=.*[0-9])" + //at least 1 digit + "(?=.*[a-z])" + //at least 1 lower case letter + "(?=.*[A-Z])" + //at least 1 upper case letter + "(?=.*[a-zA-Z])" + //any letter + "(?=.*[@#$%^&+=])" + //at least 1 special character + "(?=\\S+$)" + //no white spaces + ".{10,}" + //at least 8 characters + "$" + ) + return passwordREGEX.matcher(password).matches() + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_facebook.xml b/app/src/main/res/drawable/ic_facebook.xml new file mode 100644 index 0000000..b60ee8a --- /dev/null +++ b/app/src/main/res/drawable/ic_facebook.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_google.xml b/app/src/main/res/drawable/ic_google.xml new file mode 100644 index 0000000..61448dd --- /dev/null +++ b/app/src/main/res/drawable/ic_google.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_location.xml b/app/src/main/res/drawable/ic_location.xml new file mode 100644 index 0000000..7eb4ae9 --- /dev/null +++ b/app/src/main/res/drawable/ic_location.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_visibility.xml b/app/src/main/res/drawable/ic_visibility.xml new file mode 100644 index 0000000..f843e29 --- /dev/null +++ b/app/src/main/res/drawable/ic_visibility.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_visibility_off.xml b/app/src/main/res/drawable/ic_visibility_off.xml new file mode 100644 index 0000000..5993ca3 --- /dev/null +++ b/app/src/main/res/drawable/ic_visibility_off.xml @@ -0,0 +1,5 @@ + + + + +