App 개발 일지/Nutri Capture

Nutri Capture 프론트엔드 - bottomBar 동적 변경

interfacer_han 2024. 12. 18. 12:15

#1 개요

#1-1 소프트코딩 개요

지금까지는 +버튼을 누르면, 현재 시각을 기준으로 (하드코딩된) 더미 영양소 데이터를 담은 DayMealView 레코드가 INSERT되었다. 이제는 View에서 사용자가 시각 및 영양 데이터를 소프트코딩하여 ViewModel로 전송할 수 있게 만들어본다.

 

#1-2 채팅 UI를 위한 '채팅' 입력 창

 

Nutri Capture 방향성 - 채팅 UI

#1 기존 UI 비판#1-1 스크린샷지금까지 진행한 프로젝트의 스크린샷이다. 아래는 이 화면의 문제점들이다. #1-2 중첩된 Container'날짜 카드' Container 안에는 '식단' Container가 들어가는데, 이렇게 포장

kenel.tistory.com

이전에 계획한 방향대로 화면을 구성한다. 채팅 UI이므로 영양 데이터를 INSERT하는 방식은 하단에 고정되어있을 '채팅바'를 통해서 이뤄질 것이다.

 

#1-3 기존 UI와의 충돌

문제는 이미 하단에 NavigationBar가 존재한다는 점이다. 하단 바가 2개나 있으면 보기에도 안 좋을 뿐더러, 무엇보다 사용자 입장에서 헷갈린다. 우선순위를 따지자면 하단에 더 존재할법한 오브젝트는 '채팅바'다. NavigationBar가 수행하던 기능은 아마 TopAppBar()로 옮길 것 같다. 이 결정은 추후에 변경될 수도 있다. 그 기준은 언제나 사용자 경험이다.

 

#1-4 Scaffold의 bottomBar 분기화

 

[Android] Jetpack Compose - Scaffold

#1 개요#1-1 Scaffold의 사전적 의미Scaffold는 비계((건설) 높은 곳에서 공사를 할 수 있도록 임시로 설치한 가설물)라는 단어로 번역된다. #1-2 Scaffold in Jetpack Compose Jetpack Compose  |  Android Developers이

kenel.tistory.com

NutrientScreen을 표시하는 경우 BottomAppBar()를, 그 외의 경우에는 기존에 만들어두었던 NavigationBar()를 여전히 표시하게 나눌 것이다.

 

#2 코드

#2-1 currentBackStackEntryAsState()

 

Compose를 사용한 탐색  |  Jetpack Compose  |  Android Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. Compose를 사용한 탐색 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Navigation 구성요소는 Jetpack Compose

developer.android.com

공식 문서에서 Compose의 화면 전환(Navigation)과 bottomBar를 연결지으려면, "currentBackStackEntryAsState() 함수를 사용"하라고 한다 (참고: NavBackStackEntry). 위 공식 링크의 코드 스니펫에선 컴포저블 BottomNavigation()에서 currentBackStackEntryAsState()를 사용하고 있는데, 난 한 계층 위로 올라가 Scaffold에서 currentBackStackEntryAsState()를 사용할 것이다. 코드는 아래와 같다.

 

#2-2 Recomposition을 통한 bottomBar 동적 변경

// in MainActivity.kt

...

class MainActivity : ComponentActivity() {
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        setContent {
            NutricapturenewTheme {
                // Navigation 관리의 주체
                val navController = rememberNavController()
                val navBackStackEntry = navController.currentBackStackEntryAsState()
                val currentRoute = navBackStackEntry.value?.destination?.route

                ...

                Scaffold(
                    ...
                    bottomBar = {
                        when(currentRoute) {
                            Destination.NutrientScreen.route -> BottomAppBar { Text("test") }
                            else -> MainNavigationBar(navController)
                        }
                    },
                    ...
                ) { ... ->
                    ...
                }
            }
        }
    }

    @Composable
    private fun MainNavigationBar(navController: NavHostController) {
       ...
    }
}

...

핵심은 currentBackStackEntryAsState()다. 일까? 이름에도 나와있지만, currentBackStackEntryAsState()는 State를 반환하기 때문이다. State는 Recomposition을 유발한다. 따라서, State인 navBackStackEntry에서 파생된 변수인 currentRoute 또한 Recomposition을 위한 '감시 대상'이 된다. 이는 위 코드에 있는 when 분기문에서도 적용된다. 만약, currentRoute가 State와 연이 없는 변수였다면 내가 의도한 대로 분기문이 작동하지 않는다 (실제로 해본 결과, 분기문이 Activity의 생애주기에서 딱 한 번 실행되고 이후로는 실행되지 않는다).

 

#3 요약

Recomposition을 이용해, Destination에 따라 bottomBar가 동적 할당되게 만들었다.

 

#4 완성된 앱

#4-1 스크린샷

다음 게시글부터 "test" 부분에 채팅창을 구현한다.

 

#4-2 이 게시글 시점의 Commit

 

GitHub - Kanmanemone/nutri-capture-new

Contribute to Kanmanemone/nutri-capture-new development by creating an account on GitHub.

github.com

 

#4-3 본 프로젝트의 가장 최신 Commit

 

GitHub - Kanmanemone/nutri-capture-new

Contribute to Kanmanemone/nutri-capture-new development by creating an account on GitHub.

github.com