Skip to content

Commit ef55179

Browse files
authored
Merge pull request #52 from YAPP-Github/BOOK-124-fix/#43
fix: 바텀 네비게이션이 보이지 않는 화면으로 이동할 때, 화면이 들썩거리는 문제 해결
2 parents 75498b0 + 973e083 commit ef55179

File tree

15 files changed

+258
-112
lines changed

15 files changed

+258
-112
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.ninecraft.booket.core.ui.component
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.ColumnScope
6+
import androidx.compose.foundation.layout.fillMaxSize
7+
import androidx.compose.foundation.layout.systemBarsPadding
8+
import androidx.compose.runtime.Composable
9+
import androidx.compose.ui.Modifier
10+
import androidx.compose.ui.graphics.Color
11+
12+
/**
13+
* systemBarsPadding() 이 자동으로 적용되는 전체 화면 컨테이너
14+
* Scaffold 밖에 있는 화면에서 사용
15+
*
16+
* @param backgroundColor 화면 배경 색상
17+
*/
18+
@Composable
19+
fun ReedFullScreen(
20+
modifier: Modifier = Modifier,
21+
backgroundColor: Color = Color.White,
22+
content: @Composable ColumnScope.() -> Unit,
23+
) {
24+
Column(
25+
modifier = modifier
26+
.fillMaxSize()
27+
.background(backgroundColor)
28+
.systemBarsPadding(),
29+
) {
30+
content()
31+
}
32+
}

feature/login/src/main/kotlin/com/ninecraft/booket/feature/login/LoginPresenter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class LoginPresenter @AssistedInject constructor(
5252
isLoading = true
5353
repository.login(event.accessToken)
5454
.onSuccess {
55-
navigator.goTo(TermsAgreementScreen)
55+
navigator.resetRoot(TermsAgreementScreen)
5656
}.onFailure { exception ->
5757
exception.message?.let { Logger.e(it) }
5858
sideEffect = exception.message?.let {

feature/login/src/main/kotlin/com/ninecraft/booket/feature/login/LoginScreen.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Column
77
import androidx.compose.foundation.layout.fillMaxSize
88
import androidx.compose.foundation.layout.fillMaxWidth
99
import androidx.compose.foundation.layout.padding
10+
import androidx.compose.foundation.layout.systemBarsPadding
1011
import androidx.compose.material3.CircularProgressIndicator
1112
import androidx.compose.material3.Icon
1213
import androidx.compose.material3.Text
@@ -41,7 +42,8 @@ internal fun Login(
4142
Column(
4243
modifier = modifier
4344
.fillMaxSize()
44-
.background(White),
45+
.background(White)
46+
.systemBarsPadding(),
4547
horizontalAlignment = Alignment.CenterHorizontally,
4648
verticalArrangement = Arrangement.Center,
4749
) {

feature/login/src/main/kotlin/com/ninecraft/booket/feature/termsagreement/TermsAgreementPresenter.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import androidx.compose.runtime.getValue
66
import androidx.compose.runtime.mutableStateOf
77
import androidx.compose.runtime.remember
88
import androidx.compose.runtime.setValue
9-
import com.ninecraft.booket.screens.HomeScreen
9+
import com.ninecraft.booket.screens.BottomNavigationScreen
1010
import com.ninecraft.booket.screens.TermsAgreementScreen
1111
import com.slack.circuit.codegen.annotations.CircuitInject
1212
import com.slack.circuit.retained.rememberRetained
@@ -55,7 +55,7 @@ class TermsAgreementPresenter @AssistedInject constructor(
5555
}
5656

5757
is TermsAgreementUiEvent.OnStartButtonClick -> {
58-
navigator.resetRoot(HomeScreen)
58+
navigator.resetRoot(BottomNavigationScreen)
5959
}
6060
}
6161
}

feature/login/src/main/kotlin/com/ninecraft/booket/feature/termsagreement/TermsAgreementScreen.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.fillMaxSize
99
import androidx.compose.foundation.layout.fillMaxWidth
1010
import androidx.compose.foundation.layout.height
1111
import androidx.compose.foundation.layout.padding
12+
import androidx.compose.foundation.layout.systemBarsPadding
1213
import androidx.compose.foundation.layout.width
1314
import androidx.compose.foundation.shape.RoundedCornerShape
1415
import androidx.compose.material3.Icon
@@ -47,7 +48,8 @@ internal fun TermsAgreement(
4748
Column(
4849
modifier = modifier
4950
.fillMaxSize()
50-
.background(White),
51+
.background(White)
52+
.systemBarsPadding(),
5153
) {
5254
ReedBackTopAppBar(
5355
onBackClick = {

feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/MainActivity.kt

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,17 @@ import androidx.activity.compose.setContent
66
import androidx.activity.enableEdgeToEdge
77
import androidx.compose.foundation.isSystemInDarkTheme
88
import androidx.compose.foundation.layout.fillMaxSize
9-
import androidx.compose.foundation.layout.padding
109
import androidx.compose.runtime.DisposableEffect
1110
import androidx.compose.ui.Modifier
1211
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
13-
import com.ninecraft.booket.core.designsystem.component.ReedScaffold
12+
import com.ninecraft.booket.core.designsystem.theme.ReedTheme
1413
import com.ninecraft.booket.core.designsystem.theme.White
15-
import com.ninecraft.booket.feature.main.component.MainBottomBar
1614
import com.ninecraft.booket.screens.LoginScreen
1715
import com.slack.circuit.backstack.rememberSaveableBackStack
1816
import com.slack.circuit.foundation.Circuit
1917
import com.slack.circuit.foundation.CircuitCompositionLocals
2018
import com.slack.circuit.foundation.NavigableCircuitContent
2119
import com.slack.circuit.foundation.rememberCircuitNavigator
22-
import com.slack.circuit.overlay.ContentWithOverlays
2320
import dagger.hilt.android.AndroidEntryPoint
2421
import tech.thdev.compose.exteions.system.ui.controller.rememberSystemUiController
2522
import javax.inject.Inject
@@ -36,8 +33,6 @@ class MainActivity : ComponentActivity() {
3633

3734
setContent {
3835
val systemUiController = rememberSystemUiController()
39-
val backStack = rememberSaveableBackStack(root = LoginScreen)
40-
val navigator = rememberCircuitNavigator(backStack)
4136
val isDarkTheme = isSystemInDarkTheme()
4237

4338
DisposableEffect(systemUiController) {
@@ -50,25 +45,16 @@ class MainActivity : ComponentActivity() {
5045
onDispose {}
5146
}
5247

53-
CircuitCompositionLocals(circuit) {
54-
ReedScaffold(
55-
modifier = Modifier.fillMaxSize(),
56-
bottomBar = {
57-
MainBottomBar(
58-
navigator = navigator,
59-
backStack = backStack,
60-
)
61-
},
62-
) { innerPadding ->
63-
ContentWithOverlays {
64-
NavigableCircuitContent(
65-
navigator = navigator,
66-
backStack = backStack,
67-
modifier = Modifier
68-
.fillMaxSize()
69-
.padding(innerPadding),
70-
)
71-
}
48+
ReedTheme {
49+
val backStack = rememberSaveableBackStack(root = LoginScreen)
50+
val navigator = rememberCircuitNavigator(backStack)
51+
52+
CircuitCompositionLocals(circuit) {
53+
NavigableCircuitContent(
54+
navigator = navigator,
55+
backStack = backStack,
56+
modifier = Modifier.fillMaxSize(),
57+
)
7258
}
7359
}
7460
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.ninecraft.booket.feature.main.bottomnavigation
2+
3+
import androidx.compose.foundation.layout.fillMaxSize
4+
import androidx.compose.foundation.layout.padding
5+
import androidx.compose.runtime.Composable
6+
import androidx.compose.ui.Modifier
7+
import com.ninecraft.booket.core.designsystem.component.ReedScaffold
8+
import com.ninecraft.booket.feature.main.component.MainBottomBar
9+
import com.ninecraft.booket.feature.main.component.MainTab
10+
import com.ninecraft.booket.screens.BottomNavigationScreen
11+
import com.slack.circuit.codegen.annotations.CircuitInject
12+
import com.slack.circuit.foundation.NavigableCircuitContent
13+
import dagger.hilt.android.components.ActivityRetainedComponent
14+
import kotlinx.collections.immutable.toImmutableList
15+
16+
@CircuitInject(BottomNavigationScreen::class, ActivityRetainedComponent::class)
17+
@Composable
18+
fun BottomNavigation(
19+
state: BottomNavigationUiState,
20+
modifier: Modifier = Modifier,
21+
) {
22+
ReedScaffold(
23+
modifier = modifier.fillMaxSize(),
24+
bottomBar = {
25+
MainBottomBar(
26+
tabs = MainTab.entries.toImmutableList(),
27+
currentTab = state.currentTab,
28+
onTabSelected = { tab ->
29+
state.eventSink(BottomNavigationUiEvent.OnTabSelected(tab))
30+
},
31+
)
32+
},
33+
) { innerPadding ->
34+
NavigableCircuitContent(
35+
navigator = state.navigator,
36+
backStack = state.backStack,
37+
modifier = Modifier
38+
.fillMaxSize()
39+
.padding(innerPadding),
40+
)
41+
}
42+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.ninecraft.booket.feature.main.bottomnavigation
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.remember
5+
import com.ninecraft.booket.feature.main.component.getCurrentTab
6+
import com.ninecraft.booket.screens.BottomNavigationScreen
7+
import com.ninecraft.booket.screens.HomeScreen
8+
import com.slack.circuit.backstack.rememberSaveableBackStack
9+
import com.slack.circuit.codegen.annotations.CircuitInject
10+
import com.slack.circuit.foundation.rememberCircuitNavigator
11+
import com.slack.circuit.runtime.Navigator
12+
import com.slack.circuit.runtime.presenter.Presenter
13+
import dagger.assisted.Assisted
14+
import dagger.assisted.AssistedFactory
15+
import dagger.assisted.AssistedInject
16+
import dagger.hilt.android.components.ActivityRetainedComponent
17+
18+
class BottomNavigationPresenter @AssistedInject constructor(
19+
@Assisted private val rootNavigator: Navigator,
20+
) : Presenter<BottomNavigationUiState> {
21+
22+
@Composable
23+
override fun present(): BottomNavigationUiState {
24+
val childBackStack = rememberSaveableBackStack(root = HomeScreen)
25+
val childNavigator = rememberCircuitNavigator(childBackStack)
26+
val delegateNavigator = remember(childNavigator, rootNavigator) {
27+
DelegateNavigator(childNavigator, rootNavigator)
28+
}
29+
val currentTab = getCurrentTab(childBackStack)
30+
31+
fun handleEvent(event: BottomNavigationUiEvent) {
32+
when (event) {
33+
is BottomNavigationUiEvent.OnTabSelected -> {
34+
childNavigator.resetRoot(
35+
newRoot = event.tab.screen,
36+
saveState = true,
37+
restoreState = true,
38+
)
39+
}
40+
}
41+
}
42+
43+
return BottomNavigationUiState(
44+
backStack = childBackStack,
45+
navigator = delegateNavigator,
46+
currentTab = currentTab,
47+
eventSink = ::handleEvent,
48+
)
49+
}
50+
51+
@CircuitInject(BottomNavigationScreen::class, ActivityRetainedComponent::class)
52+
@AssistedFactory
53+
fun interface Factory {
54+
fun create(rootNavigator: Navigator): BottomNavigationPresenter
55+
}
56+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.ninecraft.booket.feature.main.bottomnavigation
2+
3+
import com.ninecraft.booket.feature.main.component.MainTab
4+
import com.slack.circuit.backstack.SaveableBackStack
5+
import com.slack.circuit.runtime.CircuitUiState
6+
import com.slack.circuit.runtime.Navigator
7+
8+
data class BottomNavigationUiState(
9+
val backStack: SaveableBackStack,
10+
val navigator: Navigator,
11+
val currentTab: MainTab?,
12+
val eventSink: (BottomNavigationUiEvent) -> Unit,
13+
) : CircuitUiState
14+
15+
sealed interface BottomNavigationUiEvent {
16+
data class OnTabSelected(val tab: MainTab) : BottomNavigationUiEvent
17+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.ninecraft.booket.feature.main.bottomnavigation
2+
3+
import com.ninecraft.booket.feature.main.component.MainTab
4+
import com.slack.circuit.runtime.Navigator
5+
import com.slack.circuit.runtime.screen.PopResult
6+
import com.slack.circuit.runtime.screen.Screen
7+
import kotlinx.collections.immutable.ImmutableList
8+
9+
class DelegateNavigator(
10+
private val childNavigator: Navigator,
11+
private val rootNavigator: Navigator,
12+
) : Navigator {
13+
private fun isMainTabScreen(screen: Screen): Boolean {
14+
return MainTab.entries.any { it.screen::class == screen::class }
15+
}
16+
17+
override fun goTo(screen: Screen): Boolean {
18+
return if (isMainTabScreen(screen)) {
19+
childNavigator.goTo(screen)
20+
} else {
21+
rootNavigator.goTo(screen)
22+
}
23+
}
24+
25+
override fun pop(result: PopResult?): Screen? {
26+
return childNavigator.pop(result)
27+
}
28+
29+
override fun peek(): Screen? {
30+
return childNavigator.peek()
31+
}
32+
33+
override fun resetRoot(
34+
newRoot: Screen,
35+
saveState: Boolean,
36+
restoreState: Boolean,
37+
): ImmutableList<Screen> {
38+
return if (isMainTabScreen(newRoot)) {
39+
childNavigator.resetRoot(newRoot, saveState, restoreState)
40+
} else {
41+
rootNavigator.resetRoot(newRoot, saveState, restoreState)
42+
}
43+
}
44+
45+
override fun peekBackStack(): ImmutableList<Screen> {
46+
return childNavigator.peekBackStack()
47+
}
48+
}

0 commit comments

Comments
 (0)