Skip to content

Commit fd0083a

Browse files
authored
Merge pull request #64 from near-Contact-Reminder/fix/fixed-onboading-ui
[FIX] 튜토리얼 화면 기기별 대응
2 parents b110105 + fca9f85 commit fd0083a

12 files changed

Lines changed: 203 additions & 155 deletions

File tree

Near/app/src/main/java/com/alarmy/near/presentation/feature/onboarding/OnboardingScreen.kt

Lines changed: 76 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,13 @@ import androidx.compose.foundation.shape.RoundedCornerShape
1717
import androidx.compose.material3.Text
1818
import androidx.compose.runtime.Composable
1919
import androidx.compose.runtime.LaunchedEffect
20-
import androidx.compose.runtime.collectAsState
21-
import androidx.compose.runtime.getValue
2220
import androidx.compose.runtime.remember
2321
import androidx.compose.runtime.rememberCoroutineScope
2422
import androidx.compose.ui.Alignment
2523
import androidx.compose.ui.Modifier
2624
import androidx.compose.ui.draw.clip
2725
import androidx.compose.ui.graphics.Color
26+
import androidx.compose.ui.layout.ContentScale
2827
import androidx.compose.ui.platform.LocalDensity
2928
import androidx.compose.ui.res.painterResource
3029
import androidx.compose.ui.res.stringResource
@@ -34,41 +33,52 @@ import androidx.compose.ui.text.buildAnnotatedString
3433
import androidx.compose.ui.text.style.TextAlign
3534
import androidx.compose.ui.text.withStyle
3635
import androidx.compose.ui.tooling.preview.Preview
36+
import androidx.compose.ui.tooling.preview.PreviewParameter
3737
import androidx.compose.ui.unit.dp
3838
import androidx.compose.ui.unit.sp
3939
import androidx.hilt.navigation.compose.hiltViewModel
40+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
4041
import com.alarmy.near.R
41-
import com.alarmy.near.presentation.feature.onboarding.components.BackgroundArea
4242
import com.alarmy.near.presentation.feature.onboarding.components.OnboardingButton
4343
import com.alarmy.near.presentation.feature.onboarding.components.PageIndicator
4444
import com.alarmy.near.presentation.feature.onboarding.model.OnboardingPage
45+
import com.alarmy.near.presentation.preview.DevicePreviewParameterProvider
46+
import com.alarmy.near.presentation.preview.component.DevicePreviewFrame
47+
import com.alarmy.near.presentation.preview.model.DevicePreviewSpec
4548
import com.alarmy.near.presentation.ui.component.NearFrame
4649
import com.alarmy.near.presentation.ui.theme.NearTheme
4750
import kotlinx.coroutines.launch
4851

49-
/**
50-
* 온보딩 화면 메인 컴포넌트
51-
* 5페이지로 구성된 뷰페이저 형태의 온보딩 화면
52-
*/
5352
@Composable
54-
fun OnboardingScreen(
53+
fun OnboardingRoute(
5554
onNavigateToLogin: () -> Unit,
5655
viewModel: OnboardingViewModel = hiltViewModel(),
5756
) {
58-
// UI 상태 관찰
59-
val uiState by viewModel.uiState.collectAsState()
57+
val uiState = viewModel.uiState.collectAsStateWithLifecycle()
6058

61-
// 사이드 이펙트 처리
6259
LaunchedEffect(Unit) {
6360
viewModel.effect.collect { effect ->
6461
when (effect) {
65-
is OnboardingEffect.NavigateToLogin -> {
66-
onNavigateToLogin()
67-
}
62+
is OnboardingEffect.NavigateToLogin -> onNavigateToLogin()
6863
}
6964
}
7065
}
7166

67+
OnboardingScreen(
68+
state = uiState.value,
69+
onCompleteOnboarding = { viewModel.completeOnboarding() },
70+
)
71+
}
72+
73+
/**
74+
* 온보딩 화면 메인 컴포넌트
75+
* 5페이지로 구성된 뷰페이저 형태의 온보딩 화면
76+
*/
77+
@Composable
78+
fun OnboardingScreen(
79+
state: OnboardingUiState,
80+
onCompleteOnboarding: () -> Unit = {},
81+
) {
7282
// 온보딩 페이지 데이터 - remember로 성능 최적화
7383
val pages =
7484
remember {
@@ -99,61 +109,63 @@ fun OnboardingScreen(
99109
// 상태바와 네비게이션 바 높이 계산
100110
val density = LocalDensity.current
101111
val statusBarHeightDp = with(density) { WindowInsets.statusBars.getTop(density).toDp() }
102-
val navigationBarHeightDp = with(density) { WindowInsets.navigationBars.getBottom(density).toDp() }
112+
val navigationBarHeightDp =
113+
with(density) { WindowInsets.navigationBars.getBottom(density).toDp() }
103114

104115
// 페이저 상태 관리
105116
val pagerState = rememberPagerState(pageCount = { pages.size })
106117
val scope = rememberCoroutineScope()
107118

108-
NearFrame {
109-
Box(
110-
modifier = Modifier.fillMaxSize(),
111-
) {
112-
BackgroundArea()
119+
NearFrame(applySystemBarsPadding = false) {
120+
Box(modifier = Modifier.fillMaxSize()) {
121+
Image(
122+
modifier = Modifier.fillMaxSize(),
123+
painter = painterResource(R.drawable.onboarding_bg_img),
124+
contentDescription = null,
125+
contentScale = ContentScale.FillBounds,
126+
)
113127
Column(
114128
modifier =
115129
Modifier
130+
.fillMaxSize()
116131
.padding(top = statusBarHeightDp, bottom = navigationBarHeightDp),
117-
horizontalAlignment = Alignment.CenterHorizontally,
118132
) {
119-
// 뷰페이저
120-
HorizontalPager(
121-
state = pagerState,
122-
) { page ->
123-
OnboardingPageContent(
124-
page = pages[page],
125-
modifier = Modifier.fillMaxWidth(),
133+
Column(
134+
modifier = Modifier.weight(1f),
135+
horizontalAlignment = Alignment.CenterHorizontally,
136+
) {
137+
HorizontalPager(
138+
modifier = Modifier.weight(1f),
139+
state = pagerState,
140+
) { page ->
141+
OnboardingPageContent(
142+
page = pages[page],
143+
modifier = Modifier.fillMaxSize(),
144+
)
145+
}
146+
PageIndicator(
147+
pageCount = pages.size,
148+
currentPage = pagerState.currentPage,
126149
)
127150
}
128-
129151
Spacer(modifier = Modifier.size(24.dp))
130-
131-
// 페이지 인디케이터
132-
PageIndicator(
133-
pageCount = pages.size,
134-
currentPage = pagerState.currentPage,
135-
)
136-
137-
Spacer(modifier = Modifier.size(14.dp))
138-
}
139-
Column(modifier = Modifier.align(Alignment.BottomCenter)) {
140-
// 다음/완료 버튼
141-
OnboardingButton(
142-
currentPage = pagerState.currentPage,
143-
totalPages = pages.size,
144-
isLoading = uiState.isLoading,
145-
onNextClick = {
146-
if (pagerState.currentPage < pages.size - 1) {
147-
scope.launch {
148-
pagerState.animateScrollToPage(pagerState.currentPage + 1)
152+
Column(horizontalAlignment = Alignment.CenterHorizontally) {
153+
OnboardingButton(
154+
currentPage = pagerState.currentPage,
155+
totalPages = pages.size,
156+
isLoading = state.isLoading,
157+
onNextClick = {
158+
if (pagerState.currentPage < pages.size - 1) {
159+
scope.launch {
160+
pagerState.animateScrollToPage(pagerState.currentPage + 1)
161+
}
162+
} else {
163+
onCompleteOnboarding()
149164
}
150-
} else {
151-
// 온보딩 완료 시 DataStore에 저장
152-
viewModel.completeOnboarding()
153-
}
154-
},
155-
)
156-
Spacer(modifier = Modifier.size(24.dp))
165+
},
166+
)
167+
Spacer(modifier = Modifier.size(24.dp))
168+
}
157169
}
158170
}
159171
}
@@ -172,7 +184,7 @@ private fun OnboardingPageContent(
172184
modifier = modifier,
173185
horizontalAlignment = Alignment.CenterHorizontally,
174186
) {
175-
Spacer(modifier = Modifier.size(22.dp))
187+
Spacer(modifier = Modifier.size(44.dp))
176188

177189
// 각 온보딩 페이지 타이틀
178190
Text(
@@ -184,16 +196,17 @@ private fun OnboardingPageContent(
184196
lineHeight = 30.sp,
185197
),
186198
)
187-
188199
Spacer(modifier = Modifier.size(16.dp))
189-
190200
Image(
191201
modifier =
192202
Modifier
203+
.weight(1f)
204+
.fillMaxWidth()
193205
.clip(RoundedCornerShape(12.dp)),
194206
painter = painterResource(page.image),
195207
contentDescription = null,
196208
)
209+
Spacer(modifier = Modifier.size(16.dp))
197210
}
198211
}
199212

@@ -235,10 +248,12 @@ private fun AnnotatedString.Builder.appendStyledText(
235248

236249
@Preview(showBackground = true)
237250
@Composable
238-
fun OnboardingScreenPreview() {
239-
NearTheme {
251+
fun OnboardingScreenPreview(
252+
@PreviewParameter(DevicePreviewParameterProvider::class) spec: DevicePreviewSpec,
253+
) {
254+
DevicePreviewFrame(spec = spec) {
240255
OnboardingScreen(
241-
onNavigateToLogin = {},
256+
state = OnboardingUiState(),
242257
)
243258
}
244259
}

Near/app/src/main/java/com/alarmy/near/presentation/feature/onboarding/components/BackgroundArea.kt

Lines changed: 0 additions & 85 deletions
This file was deleted.

Near/app/src/main/java/com/alarmy/near/presentation/feature/onboarding/navigation/NavigationOnboarding.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package com.alarmy.near.presentation.feature.onboarding.navigation
33
import androidx.navigation.NavController
44
import androidx.navigation.NavGraphBuilder
55
import androidx.navigation.compose.composable
6-
import com.alarmy.near.presentation.feature.onboarding.OnboardingScreen
6+
import com.alarmy.near.presentation.feature.onboarding.OnboardingRoute
77
import kotlinx.serialization.Serializable
88

99
@Serializable
@@ -19,7 +19,7 @@ fun NavController.navigateToOnboarding() {
1919
// 온보딩 네비게이션 그래프
2020
fun NavGraphBuilder.onboardingNavGraph(onNavigateToLogin: () -> Unit) {
2121
composable<RouteOnboarding> {
22-
OnboardingScreen(
22+
OnboardingRoute(
2323
onNavigateToLogin = onNavigateToLogin,
2424
)
2525
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.alarmy.near.presentation.preview
2+
3+
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
4+
import com.alarmy.near.presentation.preview.model.DevicePreviewSpec
5+
6+
/**
7+
* 삼성 대표 기기 해상도를 기반으로 Compose 프리뷰에서 사용할 dp 값을 제공합니다.
8+
*/
9+
class DevicePreviewParameterProvider : PreviewParameterProvider<DevicePreviewSpec> {
10+
override val values: Sequence<DevicePreviewSpec> =
11+
sequenceOf(
12+
DevicePreviewSpec(
13+
deviceName = "Galaxy S21 (FHD+)",
14+
widthDp = 360,
15+
heightDp = 800,
16+
),
17+
DevicePreviewSpec(
18+
deviceName = "Galaxy S23 (FHD+)",
19+
widthDp = 360,
20+
heightDp = 780,
21+
),
22+
DevicePreviewSpec(
23+
deviceName = "Galaxy S25 (QHD+)",
24+
widthDp = 412,
25+
heightDp = 915,
26+
),
27+
DevicePreviewSpec(
28+
deviceName = "Galaxy S23 Ultra (QHD+)",
29+
widthDp = 411,
30+
heightDp = 915,
31+
),
32+
DevicePreviewSpec(
33+
deviceName = "Galaxy Z Flip (FHD+)",
34+
widthDp = 360,
35+
heightDp = 860,
36+
),
37+
DevicePreviewSpec(
38+
deviceName = "Galaxy Z Fold (QXGA+)",
39+
widthDp = 600,
40+
heightDp = 730,
41+
),
42+
DevicePreviewSpec(
43+
deviceName = "Galaxy A60 (FHD+)",
44+
widthDp = 360,
45+
heightDp = 780,
46+
),
47+
)
48+
}

0 commit comments

Comments
 (0)