Skip to content

Commit 7b4f7b3

Browse files
committed
feat: Snackbar 기능 추가
게시글 생성, 수정, 삭제 시 사용자에게 피드백을 제공하기 위해 Snackbar를 추가했습니다. * **MainActivity**: `Scaffold`와 `SnackbarHost`를 설정하여 앱 전체에서 Snackbar를 사용할 수 있도록 UI 구조를 변경했습니다. * **NavGraph**: 각 화면(Screen)으로 `SnackbarHostState`를 전달하도록 수정했습니다. * **PostCreateScreen, PostEditScreen, PostDetailScreen**: 게시글 생성, 수정, 삭제 작업이 성공적으로 완료되면 Coroutine Scope 내에서 Snackbar를 호출하여 사용자에게 "게시글이 작성되었습니다.", "게시글이 수정되었습니다.", "게시글이 삭제되었습니다."와 같은 메시지를 표시하도록 구현했습니다.
1 parent c62340d commit 7b4f7b3

File tree

6 files changed

+68
-18
lines changed

6 files changed

+68
-18
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
kotlin version: 2.0.21
2+
error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output:
3+
1. Kotlin compile daemon is ready
4+

app/src/main/java/com/example/kuit6_android_api/MainActivity.kt

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@ import android.widget.Toast
88
import androidx.activity.ComponentActivity
99
import androidx.activity.compose.setContent
1010
import androidx.activity.result.contract.ActivityResultContracts
11+
import androidx.compose.foundation.background
1112
import androidx.compose.foundation.layout.fillMaxSize
1213
import androidx.compose.material3.MaterialTheme
14+
import androidx.compose.material3.Scaffold
15+
import androidx.compose.material3.SnackbarHost
16+
import androidx.compose.material3.SnackbarHostState
1317
import androidx.compose.material3.Surface
18+
import androidx.compose.runtime.remember
1419
import androidx.compose.ui.Modifier
1520
import androidx.core.content.ContextCompat
1621
import androidx.navigation.compose.rememberNavController
@@ -40,15 +45,23 @@ class MainActivity : ComponentActivity() {
4045

4146
setContent {
4247
KUIT6_Android_APITheme {
43-
Surface(
44-
modifier = Modifier.fillMaxSize(),
45-
color = MaterialTheme.colorScheme.background
48+
val snackBarState = remember { SnackbarHostState() }
49+
Scaffold(
50+
modifier = Modifier
51+
.fillMaxSize()
52+
.background(
53+
MaterialTheme.colorScheme.background
54+
),
55+
snackbarHost = {
56+
SnackbarHost(hostState = snackBarState)
57+
}
4658
) {
4759
val navController = rememberNavController()
4860

4961
NavGraph(
5062
navController = navController,
51-
startDestination = PostListRoute
63+
startDestination = PostListRoute,
64+
snackBarState = snackBarState
5265
)
5366
}
5467
}
@@ -71,6 +84,7 @@ class MainActivity : ComponentActivity() {
7184
) == PackageManager.PERMISSION_GRANTED -> {
7285
// 이미 권한이 있음
7386
}
87+
7488
shouldShowRequestPermissionRationale(permission) -> {
7589
// 권한 거부 이력이 있음 - 설명 표시 후 재요청
7690
Toast.makeText(
@@ -80,6 +94,7 @@ class MainActivity : ComponentActivity() {
8094
).show()
8195
requestPermissionLauncher.launch(permission)
8296
}
97+
8398
else -> {
8499
// 권한 요청
85100
requestPermissionLauncher.launch(permission)

app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.example.kuit6_android_api.ui.navigation
22

3+
import androidx.compose.material3.SnackbarHostState
34
import androidx.compose.runtime.Composable
45
import androidx.navigation.NavHostController
56
import androidx.navigation.compose.NavHost
@@ -13,7 +14,8 @@ import com.example.kuit6_android_api.ui.post.screen.PostListScreen
1314
@Composable
1415
fun NavGraph(
1516
navController: NavHostController,
16-
startDestination: Any = PostListRoute
17+
startDestination: Any = PostListRoute,
18+
snackBarState: SnackbarHostState
1719
) {
1820
NavHost(
1921
navController = navController,
@@ -40,7 +42,8 @@ fun NavGraph(
4042
},
4143
onEditClick = { postId ->
4244
navController.navigate(PostEditRoute(postId))
43-
}
45+
},
46+
snackBarState = snackBarState
4447
)
4548
}
4649

@@ -51,7 +54,8 @@ fun NavGraph(
5154
},
5255
onPostCreated = {
5356
navController.popBackStack()
54-
}
57+
},
58+
snackBarState = snackBarState
5559
)
5660
}
5761

@@ -65,7 +69,8 @@ fun NavGraph(
6569
},
6670
onPostUpdated = {
6771
navController.popBackStack()
68-
}
72+
},
73+
snackBarState = snackBarState
6974
)
7075
}
7176
}

app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostCreateScreen.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@ import androidx.compose.material3.MaterialTheme
3333
import androidx.compose.material3.OutlinedTextField
3434
import androidx.compose.material3.OutlinedTextFieldDefaults
3535
import androidx.compose.material3.Scaffold
36+
import androidx.compose.material3.SnackbarHostState
3637
import androidx.compose.material3.Text
3738
import androidx.compose.material3.TopAppBar
3839
import androidx.compose.material3.TopAppBarDefaults
3940
import androidx.compose.runtime.Composable
4041
import androidx.compose.runtime.getValue
4142
import androidx.compose.runtime.mutableStateOf
4243
import androidx.compose.runtime.remember
44+
import androidx.compose.runtime.rememberCoroutineScope
4345
import androidx.compose.runtime.setValue
4446
import androidx.compose.ui.Alignment
4547
import androidx.compose.ui.Modifier
@@ -52,19 +54,23 @@ import androidx.compose.ui.unit.dp
5254
import androidx.lifecycle.viewmodel.compose.viewModel
5355
import coil.compose.AsyncImage
5456
import com.example.kuit6_android_api.ui.post.viewmodel.PostViewModel
57+
import kotlinx.coroutines.launch
5558

5659
@OptIn(ExperimentalMaterial3Api::class)
5760
@Composable
5861
fun PostCreateScreen(
5962
onNavigateBack: () -> Unit,
6063
onPostCreated: () -> Unit,
61-
viewModel: PostViewModel = viewModel()
64+
viewModel: PostViewModel = viewModel(),
65+
snackBarState: SnackbarHostState
6266
) {
6367
val context = LocalContext.current
6468
var author by remember { mutableStateOf("") }
6569
var title by remember { mutableStateOf("") }
6670
var content by remember { mutableStateOf("") }
6771
var selectedImageUri by remember { mutableStateOf<Uri?>(null) }
72+
val scope = rememberCoroutineScope()
73+
6874

6975
val imagePickerLauncher = rememberLauncherForActivityResult(
7076
contract = ActivityResultContracts.GetContent()
@@ -266,6 +272,7 @@ fun PostCreateScreen(
266272
val finalAuthor = author
267273
viewModel.createPost(finalAuthor, title, content, viewModel.uploadedImageUrl) {
268274
onPostCreated()
275+
scope.launch { snackBarState.showSnackbar("게시글이 작성되었습니다.") }
269276
}
270277
},
271278
modifier = Modifier
@@ -302,7 +309,8 @@ fun PostCreateScreenPreview() {
302309
MaterialTheme {
303310
PostCreateScreen(
304311
onNavigateBack = {},
305-
onPostCreated = {}
312+
onPostCreated = {},
313+
snackBarState = remember{ SnackbarHostState()}
306314
)
307315
}
308316
}

app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostDetailScreen.kt

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@ import androidx.compose.material3.Scaffold
2828
import androidx.compose.material3.Surface
2929
import androidx.compose.material3.Text
3030
import androidx.compose.material3.TextButton
31+
import androidx.compose.material3.SnackbarHostState
3132
import androidx.compose.material3.TopAppBar
3233
import androidx.compose.runtime.Composable
3334
import androidx.compose.runtime.LaunchedEffect
3435
import androidx.compose.runtime.getValue
3536
import androidx.compose.runtime.mutableStateOf
3637
import androidx.compose.runtime.remember
38+
import androidx.compose.runtime.rememberCoroutineScope
3739
import androidx.compose.runtime.setValue
3840
import androidx.compose.ui.Alignment
3941
import androidx.compose.ui.Modifier
@@ -46,17 +48,20 @@ import androidx.lifecycle.viewmodel.compose.viewModel
4648
import coil.compose.AsyncImage
4749
import com.example.kuit6_android_api.ui.post.viewmodel.PostViewModel
4850
import com.example.kuit6_android_api.util.formatDateTime
51+
import kotlinx.coroutines.launch
4952

5053
@OptIn(ExperimentalMaterial3Api::class)
5154
@Composable
5255
fun PostDetailScreen(
5356
postId: Long,
5457
onNavigateBack: () -> Unit,
5558
onEditClick: (Long) -> Unit = {},
56-
viewModel: PostViewModel = viewModel()
59+
viewModel: PostViewModel = viewModel(),
60+
snackBarState: SnackbarHostState
5761
) {
5862
val post = viewModel.postDetail
5963
var showDeleteDialog by remember { mutableStateOf(false) }
64+
val scope = rememberCoroutineScope()
6065

6166
LaunchedEffect(postId) {
6267
// viewModel.getPostDetail(postId)
@@ -194,10 +199,13 @@ fun PostDetailScreen(
194199
text = { Text("정말로 이 게시글을 삭제하시겠습니까?") },
195200
confirmButton = {
196201
TextButton(onClick = {
197-
// viewModel.deletePost(postId) {
198-
// showDeleteDialog = false
199-
// onNavigateBack()
200-
// }
202+
viewModel.deletePost(postId) {
203+
showDeleteDialog = false
204+
scope.launch {
205+
snackBarState.showSnackbar("게시글이 삭제되었습니다.")
206+
}
207+
onNavigateBack()
208+
}
201209
}) {
202210
Text("삭제")
203211
}
@@ -219,7 +227,8 @@ fun PostDetailScreenPreview() {
219227
PostDetailScreen(
220228
postId = 1L,
221229
onNavigateBack = {},
222-
onEditClick = {}
230+
onEditClick = {},
231+
snackBarState = remember { SnackbarHostState() }
223232
)
224233
}
225234
}

app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostEditScreen.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@ import androidx.compose.material3.OutlinedButton
3333
import androidx.compose.material3.OutlinedTextField
3434
import androidx.compose.material3.OutlinedTextFieldDefaults
3535
import androidx.compose.material3.Scaffold
36+
import androidx.compose.material3.SnackbarHostState
3637
import androidx.compose.material3.Text
3738
import androidx.compose.material3.TopAppBar
3839
import androidx.compose.runtime.Composable
3940
import androidx.compose.runtime.LaunchedEffect
4041
import androidx.compose.runtime.getValue
4142
import androidx.compose.runtime.mutableStateOf
4243
import androidx.compose.runtime.remember
44+
import androidx.compose.runtime.rememberCoroutineScope
4345
import androidx.compose.runtime.setValue
4446
import androidx.compose.ui.Alignment
4547
import androidx.compose.ui.Modifier
@@ -51,17 +53,20 @@ import androidx.compose.ui.unit.dp
5153
import androidx.lifecycle.viewmodel.compose.viewModel
5254
import coil.compose.AsyncImage
5355
import com.example.kuit6_android_api.ui.post.viewmodel.PostViewModel
56+
import kotlinx.coroutines.launch
5457

5558
@OptIn(ExperimentalMaterial3Api::class)
5659
@Composable
5760
fun PostEditScreen(
5861
postId: Long,
5962
onNavigateBack: () -> Unit,
6063
onPostUpdated: () -> Unit,
61-
viewModel: PostViewModel = viewModel()
64+
viewModel: PostViewModel = viewModel(),
65+
snackBarState: SnackbarHostState
6266
) {
6367
val context = LocalContext.current
6468
val post = viewModel.postDetail
69+
val scope = rememberCoroutineScope()
6570

6671
var title by remember { mutableStateOf("") }
6772
var content by remember { mutableStateOf("") }
@@ -231,6 +236,9 @@ fun PostEditScreen(
231236
post?.imageUrl
232237
}
233238
viewModel.updatePost(postId, title, content, imageUrl) {
239+
scope.launch {
240+
snackBarState.showSnackbar("게시글이 수정되었습니다.")
241+
}
234242
onPostUpdated()
235243
}
236244
},
@@ -277,7 +285,8 @@ fun PostEditScreenPreview() {
277285
PostEditScreen(
278286
postId = 1L,
279287
onNavigateBack = {},
280-
onPostUpdated = {}
288+
onPostUpdated = {},
289+
snackBarState = remember { SnackbarHostState() }
281290
)
282291
}
283292
}

0 commit comments

Comments
 (0)