Skip to content

Commit

Permalink
Merge pull request #16 from YAPP-Github/feature/tgyuu/PC-185
Browse files Browse the repository at this point in the history
[PC-185] Bottom Navigation Bar UI 구현
  • Loading branch information
tgyuuAn authored Dec 30, 2024
2 parents fef6e67 + 7455517 commit 7c4ec79
Show file tree
Hide file tree
Showing 23 changed files with 214 additions and 88 deletions.
3 changes: 2 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@ dependencies {
implementation(libs.kakao.user)

implementation(projects.feature.auth)
implementation(projects.feature.etc)
implementation(projects.feature.setting)
implementation(projects.feature.matching)
implementation(projects.feature.mypage)

implementation(projects.core.designsystem)
implementation(projects.core.navigation)
implementation(projects.core.commonUi)
}
4 changes: 2 additions & 2 deletions app/src/main/java/com/puzzle/piece/navigation/AppNavHost.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import com.puzzle.auth.navigation.authNavGraph
import com.puzzle.etc.navigation.etcScreen
import com.puzzle.setting.navigation.settingScreen
import com.puzzle.matching.navigation.matchingNavGraph
import com.puzzle.mypage.navigation.myPageScreen
import com.puzzle.navigation.AuthGraph
Expand All @@ -23,6 +23,6 @@ fun AppNavHost(
authNavGraph()
matchingNavGraph()
myPageScreen()
etcScreen()
settingScreen()
}
}
Original file line number Diff line number Diff line change
@@ -1,41 +1,35 @@
package com.puzzle.piece.navigation

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Call
import androidx.compose.material.icons.outlined.Call
import androidx.compose.ui.graphics.vector.ImageVector
import com.puzzle.navigation.EtcRoute
import androidx.annotation.DrawableRes
import com.puzzle.navigation.SettingRoute
import com.puzzle.navigation.MatchingGraphDest
import com.puzzle.navigation.MyPageRoute
import com.puzzle.piece.R
import kotlin.reflect.KClass

enum class TopLevelDestination(
val selectedIcon: ImageVector,
val unselectedIcon: ImageVector,
val iconText: String,
val titleText: String,
@DrawableRes val iconDrawableId: Int,
val contentDescription: String,
val title: String,
val route: KClass<*>,
) {
MATCHING(
selectedIcon = Icons.Filled.Call,
unselectedIcon = Icons.Outlined.Call,
iconText = "매칭",
titleText = "매칭",
route = MatchingGraphDest.MatchingRoute::class,
),
MY_PAGE(
selectedIcon = Icons.Filled.Call,
unselectedIcon = Icons.Outlined.Call,
iconText = "마이페이지",
titleText = "마이페이지",
iconDrawableId = R.drawable.ic_profile,
contentDescription = "프로필",
title = "프로필",
route = MyPageRoute::class,
),
ETC(
selectedIcon = Icons.Filled.Call,
unselectedIcon = Icons.Outlined.Call,
iconText = "ETC",
titleText = "ETC",
route = EtcRoute::class,
MATCHING(
iconDrawableId = R.drawable.ic_profile,
contentDescription = "매칭",
title = "매칭",
route = MatchingGraphDest.MatchingRoute::class,
),
SETTING(
iconDrawableId = R.drawable.ic_setting,
contentDescription = "설정",
title = "설정",
route = SettingRoute::class,
);

companion object {
Expand Down
109 changes: 82 additions & 27 deletions app/src/main/java/com/puzzle/piece/ui/App.kt
Original file line number Diff line number Diff line change
@@ -1,25 +1,45 @@
@file:OptIn(ExperimentalMaterial3Api::class)

package com.puzzle.piece.ui

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.material.BottomNavigation
import androidx.compose.material.BottomNavigationItem
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FabPosition
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.FloatingActionButtonDefaults.bottomAppBarFabElevation
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavDestination
import androidx.navigation.NavDestination.Companion.hasRoute
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState
import com.puzzle.common.ui.NoRippleInteractionSource
import com.puzzle.designsystem.foundation.PieceTheme
import com.puzzle.navigation.AuthGraph
import com.puzzle.navigation.EtcRoute
import com.puzzle.navigation.MatchingGraph
import com.puzzle.navigation.MatchingGraphDest.MatchingDetailRoute
import com.puzzle.navigation.MyPageRoute
import com.puzzle.navigation.Route
import com.puzzle.navigation.SettingRoute
import com.puzzle.piece.R
import com.puzzle.piece.navigation.AppNavHost
import com.puzzle.piece.navigation.TopLevelDestination
import kotlin.reflect.KClass
Expand All @@ -42,7 +62,25 @@ fun App(
navigateToTopLevelDestination = navigateToTopLevelDestination,
)
}
}
},
floatingActionButton = {
if (currentDestination?.shouldHideBottomNavigation() == false) {
FloatingActionButton(
onClick = { navigateToTopLevelDestination(MatchingGraph) },
containerColor = PieceTheme.colors.white,
shape = CircleShape,
elevation = bottomAppBarFabElevation(),
modifier = Modifier.offset(y = 84.dp),
) {
Image(
painter = painterResource(R.drawable.ic_matching),
contentDescription = null,
modifier = Modifier.size(80.dp),
)
}
}
},
floatingActionButtonPosition = FabPosition.Center,
) { innerPadding ->
val contentModifier = modifier.padding(innerPadding)

Expand All @@ -58,24 +96,51 @@ private fun AppBottomBar(
currentDestination: NavDestination?,
navigateToTopLevelDestination: (Route) -> Unit,
) {
BottomNavigation(
modifier = Modifier.navigationBarsPadding()
NavigationBar(
containerColor = PieceTheme.colors.white,
modifier = Modifier
.navigationBarsPadding()
.height(68.dp),
) {
TopLevelDestination.topLevelDestinations.forEach { topLevelRoute ->
BottomNavigationItem(
NavigationBarItem(
icon = {
Icon(
imageVector = topLevelRoute.selectedIcon,
contentDescription = topLevelRoute.name
)
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(top = 2.dp),
) {
Icon(
painter = painterResource(topLevelRoute.iconDrawableId),
contentDescription = topLevelRoute.contentDescription,
modifier = Modifier.size(32.dp),
)

Text(
text = topLevelRoute.title,
style = PieceTheme.typography.captionM,
)
}
},
label = { Text(topLevelRoute.name) },
alwaysShowLabel = false,
selected = currentDestination.isRouteInHierarchy(topLevelRoute.route),
colors = androidx.compose.material3.NavigationBarItemDefaults.colors(
selectedIconColor = PieceTheme.colors.primaryDefault,
unselectedIconColor = PieceTheme.colors.dark3,
selectedTextColor = PieceTheme.colors.primaryDefault,
unselectedTextColor = PieceTheme.colors.dark3,
indicatorColor = Color.Transparent,
),
interactionSource = remember { NoRippleInteractionSource() },
onClick = {
when (topLevelRoute) {
TopLevelDestination.MATCHING -> navigateToTopLevelDestination(MatchingGraph)
TopLevelDestination.MATCHING -> navigateToTopLevelDestination(
MatchingGraph
)

TopLevelDestination.MY_PAGE -> navigateToTopLevelDestination(MyPageRoute)
TopLevelDestination.ETC -> navigateToTopLevelDestination(EtcRoute)
TopLevelDestination.SETTING -> navigateToTopLevelDestination(
SettingRoute
)
}
},
)
Expand All @@ -85,21 +150,11 @@ private fun AppBottomBar(

private val HIDDEN_BOTTOM_NAV_ROUTES = setOf(
AuthGraph::class.qualifiedName,
MatchingDetailRoute::class.qualifiedName
MatchingDetailRoute::class.qualifiedName,
)

/**
* 현재 목적지가 바텀 네비게이션이 보여지지 않는 화면인지 확인하는 메서드
*/
private fun NavDestination?.shouldHideBottomNavigation(): Boolean =
this?.hierarchy?.any { destination ->
destination.route in HIDDEN_BOTTOM_NAV_ROUTES
} ?: false
this?.hierarchy?.any { destination -> destination.route in HIDDEN_BOTTOM_NAV_ROUTES } ?: false

/**
* 현재 목적지가 TopLevelDestination 라우트에 속하는지 확인하는 메서드
*/
private fun NavDestination?.isRouteInHierarchy(route: KClass<*>): Boolean =
this?.hierarchy?.any {
it.hasRoute(route)
} ?: false
this?.hierarchy?.any { it.hasRoute(route) } == true
17 changes: 17 additions & 0 deletions app/src/main/res/drawable/ic_matching.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="80dp"
android:height="80dp"
android:viewportWidth="80"
android:viewportHeight="80">
<path
android:pathData="M40,3L40,3A37,37 0,0 1,77 40L77,40A37,37 0,0 1,40 77L40,77A37,37 0,0 1,3 40L3,40A37,37 0,0 1,40 3z"
android:strokeWidth="6"
android:fillColor="#6F00FB"
android:strokeColor="#ffffff"/>
<path
android:pathData="M56.667,34.713C56.667,42.784 49.967,47.084 45.064,50.934C43.334,52.292 41.667,53.571 40.001,53.571C40.001,53.571 40.001,51.059 40.001,49.392C40.001,47.726 40.001,46.403 40.001,38.333C40.001,30.264 40.001,28.678 40.001,28.678C47.501,20.918 56.667,26.642 56.667,34.713Z"
android:fillColor="#D0ABFD"/>
<path
android:pathData="M33.508,49.829C28.829,46.238 23.334,42.022 23.334,34.713C23.334,26.691 32.389,20.987 39.863,28.538C39.95,28.626 40.001,28.747 40.001,28.871V36.228C40.001,36.705 40.775,36.993 41.199,36.773C41.625,36.553 42.108,36.429 42.62,36.429C44.329,36.429 45.715,37.814 45.715,39.524C45.715,41.233 44.329,42.619 42.62,42.619C42.108,42.619 41.625,42.495 41.199,42.275C40.775,42.055 40.001,42.343 40.001,42.82V49.392V53.095C40.001,53.358 39.786,53.574 39.526,53.538C38.017,53.328 36.503,52.163 34.937,50.934C34.474,50.571 33.996,50.203 33.508,49.829Z"
android:fillColor="#F6EFFF"/>
</vector>
33 changes: 33 additions & 0 deletions app/src/main/res/drawable/ic_profile.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="33dp"
android:height="32dp"
android:viewportWidth="33"
android:viewportHeight="32">
<path
android:pathData="M10.25,11H21.75"
android:strokeLineJoin="round"
android:strokeWidth="1.6"
android:fillColor="#00000000"
android:strokeColor="#909599"
android:strokeLineCap="round"/>
<path
android:pathData="M10.25,16H21.75"
android:strokeLineJoin="round"
android:strokeWidth="1.6"
android:fillColor="#00000000"
android:strokeColor="#909599"
android:strokeLineCap="round"/>
<path
android:pathData="M10.25,21H18.25"
android:strokeLineJoin="round"
android:strokeWidth="1.6"
android:fillColor="#00000000"
android:strokeColor="#909599"
android:strokeLineCap="round"/>
<path
android:pathData="M7.25,6L25.25,6A1,1 0,0 1,26.25 7L26.25,25A1,1 0,0 1,25.25 26L7.25,26A1,1 0,0 1,6.25 25L6.25,7A1,1 0,0 1,7.25 6z"
android:strokeLineJoin="round"
android:strokeWidth="1.6"
android:fillColor="#00000000"
android:strokeColor="#909599"/>
</vector>
16 changes: 16 additions & 0 deletions app/src/main/res/drawable/ic_setting.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="33dp"
android:height="32dp"
android:viewportWidth="33"
android:viewportHeight="32">
<path
android:pathData="M13.388,7.209C13.388,6.173 14.27,5.333 15.358,5.333H18.142C19.23,5.333 20.113,6.173 20.113,7.209C20.113,8.652 21.755,9.555 23.069,8.833C24.011,8.315 25.217,8.622 25.761,9.519L27.153,11.813C27.697,12.71 27.374,13.857 26.431,14.375C25.117,15.097 25.117,16.902 26.431,17.624C27.374,18.142 27.697,19.289 27.153,20.186L25.761,22.48C25.217,23.377 24.011,23.684 23.069,23.166C21.755,22.444 20.113,23.347 20.113,24.791C20.113,25.827 19.23,26.666 18.142,26.666H15.358C14.27,26.666 13.388,25.827 13.388,24.791C13.388,23.347 11.745,22.444 10.432,23.166C9.489,23.684 8.284,23.377 7.74,22.48L6.348,20.186C5.804,19.289 6.127,18.142 7.069,17.624C8.383,16.902 8.383,15.097 7.069,14.375C6.127,13.857 5.804,12.71 6.348,11.813L7.74,9.519C8.284,8.622 9.489,8.315 10.432,8.833C11.745,9.555 13.388,8.652 13.388,7.209Z"
android:strokeWidth="1.6"
android:fillColor="#00000000"
android:strokeColor="#909599"/>
<path
android:pathData="M21.233,16C21.233,18.356 19.226,20.266 16.75,20.266C14.274,20.266 12.267,18.356 12.267,16C12.267,13.643 14.274,11.733 16.75,11.733C19.226,11.733 21.233,13.643 21.233,16Z"
android:strokeWidth="1.6"
android:fillColor="#00000000"
android:strokeColor="#909599"/>
</vector>
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal fun Project.configureAndroidCompose() {

androidExtension.apply {
composeOptions {
kotlinCompilerExtensionVersion = "1.3.0"
kotlinCompilerExtensionVersion = "1.5.15"
}

buildFeatures.apply {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.puzzle.common.ui

import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.runtime.Immutable
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow

@Immutable
class NoRippleInteractionSource : MutableInteractionSource {
override suspend fun emit(interaction: Interaction) {}
override val interactions: Flow<Interaction> = emptyFlow()
override fun tryEmit(interaction: Interaction): Boolean = true
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ sealed class AuthGraphDest : Route {
}

@Serializable
data object EtcRoute : Route
data object SettingRoute : Route

@Serializable
data object MatchingGraph : Route
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -275,4 +275,4 @@ private fun PreviewMatchingScreen() {
navigateToMatchingDetail = {},
)
}
}
}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ plugins {
}

android {
namespace = "com.puzzle.etc"
namespace = "com.puzzle.setting"
}
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.puzzle.etc
package com.puzzle.setting

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
Expand Down
File renamed without changes.
Loading

0 comments on commit 7c4ec79

Please sign in to comment.