안녕하세요. Narvis2 입니다.
이번 포스팅에서는 Compose
의 화면전화에 사용되는 Compose Navigation
에 대하여 알아보겠습니다.
🍎 Compose Navigation
🍀 1. Dependency 추가 (build.gradle)
1
implementation "androidx.navigation:navigation-compose:2.5.3"
🍀 2. rememberNavController
navigation
구성요소의 중심 APIStateful
이며, 앱의 화면과 각 화면 상태를 구성하는Composable Back Stack
을 추적함- 모든
Composable
이 접근할 수 있는 곳에 만들어야 함 즉, 최상위 함수
예제 👇
- 처음 시작 시
HomeScreen
을 보여주고 Click 시DetailScreen
으로 이동NavGraph
설정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Composable
fun MovieNavigation() {
val navController = rememberNavController()
// Navigation Graph 를 그림
NavHost(
navController = navController,
startDestination = MovieScreen.HomeScreen.name // 처음 시작화면 설정
) {
composable(route = MovieScreen.HomeScreen.name) {
HomeScreen(navController = navConttoller)
}
// "/{movie}" -> DetailScreen 에서 사용할 파라미터 이름 (데이터를 넘김)
// 하나의 DetailScreen 을 여러군데에서 재사용하므로 Unique Key 필요
composable(
route = MovieScreen.DetailScreen.name + "/{movie}",
argument = listOf(navArgument(name = "movie") { type = NavType.StringType })
) {
DetailScreen(
navController = navController,
it.argument?.getString("movie")
)
}
}
}
enum class MovieScreen {
HomeScreen,
DetailScreen
}
예제 👇
Navigation
을 활용하여HomeScreen -> DetailScreen 이동
1
navController.navigate(route = MovieScreens.DetailScreen.name + "/$보내는 값")
예제 👇
Navigation
뒤로가기
1
navController.popBackStack()
🍀 3. Navigation + Dialog
예제 👇
Custom Dialog
를 생성하여Navigation
으로 띄우기
NavGraph
에Custom Dialog
등록
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@Composable
fun NoteNavigation(viewModel: NoteViewModel) {
val navController = rememberNavController()
val coroutineScope = rememberCoroutineScope()
// CustomDialog
val customDialogTitle = viewModel.customDialogTitle.collectAsState()
val customDialogConfirmText = viewModel.customDialogConfirmText.collectAsState()
val customDialogCancelText = viewModel.customDialogCancelText.collectAsState()
NavHost(
navController = navController,
startDestination = NoteNavigation.HomeScreen.name
) {
composable(route = NoteNavigation.HomeScreen.name) {
NoteScreen(navController = navController)
}
dialog(
route = NoteNavigation.CustomDialog.name,
dialogProperies = DialogProperties(
dismissOnBackPress = true,
dismissOnClickOutside = true,
)
) {
CustomDialog(
navController = navController,
value = customDialogTitle.value.first,
valueRes= customDialogTitle.value.second,
confirmText = customDialogConfirmText.value,
cancelText = customDialogCancelText.value,
) {
coroutineScope.launch {
if (customDialogTitle.value.second == R.string.dialog_all_remove_title) {
viewModel.removeAllNote()
return@launch
}
viewModel.currentNote.value?.let {
if (customDialogTitle.value.second == R.string.dialog_modify_title) {
viewModel.updateNote(it)
scaffoldState.snackbarHostState.showSnackbar("${it.title}를 수정하였습니다.")
return@launch
}
viewModel.removeNote(it)
scaffoldState.snackbarHostState.showSnackbar("${it.title}를 삭제하였습니다.")
}
}
}
}
}
}
예제 👇
Custom Dialog
생성- 참고
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
@Composable
fun CustomDialog(
navController: NavController,
value: String,
@StringRes valueRes: Int? = null,
@StringRes confirmText: Int,
@StringRes cancelText: Int,
onConfirmClick: () -> Unit,
) {
Surface(
shape = RoundedCornerShape(16.dp), color = Color.White
) {
Box(contentAlignment = Alignment.Center) {
Column {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
horizontalArrangement = Arrangement.End,
) {
Icon(imageVector = Icons.Filled.Cancel,
contentDescription = "",
modifier = Modifier
.width(30.dp)
.height(30.dp)
.clickable { navController.popBackStack() })
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = if (valueRes != null) {
stringResource(id = valueRes, value)
} else {
value
}, style = TextStyle(
fontSize = 20.sp,
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Bold
)
)
}
Spacer(modifier = Modifier.height(20.dp))
Row(
modifier = Modifier
.fillMaxWidth()
.padding(20.dp)
) {
Button(
onClick = {
navController.popBackStack()
/**
* navController.currentBackStackEntry?.destination?.route
* -> 현재 Navigation Route 이름 가져오기
*/
if (navController.currentBackStackEntry?.destination?.route == NavigationType.DETAIL_SCREEN.name) {
navController.popBackStack()
}
onConfirmClick()
},
shape = RoundedCornerShape(50.dp),
modifier = Modifier
.height(50.dp)
.padding(end = 10.dp)
.weight(1f)
) {
Text(text = stringResource(id = confirmText))
}
Button(
onClick = {
navController.popBackStack()
},
shape = RoundedCornerShape(50.dp),
modifier = Modifier
.height(50.dp)
.padding(start = 10.dp)
.weight(1f),
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.White,
contentColor = Color.Black,
)
) {
Text(
text = stringResource(id = cancelText),
style = TextStyle(color = Color.Black)
)
}
}
}
}
}
}
🍎 Compose BottomSheet Navigation
🍀 1. Dependency 추가 (build.gradle)
1
implementation "com.google.accompanist:accompanist-navigation-material:0.27.0"
🍀 2. rememberNavController, rememberBottomSheetNavigator
rememberBottomSheetNavigator
,rememberNavController
를 사용하여NavGraph
작성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Composable
fun MovieNavigation(mainViewModel: MainViewModel) {
val bottomSheetNavigator = rememberBottomSheetNavigator()
val navController = rememberNavController(bottomSheetNavigator)
ModalBottomSheetLayout(
bottomSheetNavigator = bottomSheetNavigator,
sheetShape = RoundedCornerShape(topEnd = 16.dp, topStart = 16.dp),
) {
NavHost(
navController = navController,
startDestination = NavigationType.HomeScreen.name
) {
composable(route = NavigationType.HomeScreen.name) {
HomeScreen(navController = navController)
}
bottomSheet(route = NavigationType.BottomSheet.name) {
Text("This is a cool bottom sheet!")
}
}
}
}
예제 👇 화면 전환
1
navController.navigate(route = NavigationType.BottomSheet.name)
예제 👇 뒤로가기
1
navController.popBackStack()
🍀 3. Navigating with Arguments
bottomSheet
에 데이터 전달
예제 👇
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Composable
fun MyNavigation(mainViewModel: MainViewModel) {
val navController = rememberNavController()
val bottomSheetNavigator = rememberBottomSheetNavigator()
navController.navigatorProvider += bottomSheetNavigator
ModalBottomSheetLayout(bottomSheetNavigator) {
NavHost(navController, startDestination = "home") {
composable(route = "home") {
Button(onClick = {
navController.navigate("sheet?message=hellow_world")
}) {
Text("Click me to see something cool!")
}
}
bottomSheet(route = "sheet?message={message}") { backStackEntry ->
val message = backStackEntry.arguments?.getString("message")
Text("This is a cool bottom Sheet")
if (message != null) {
Text("Magic message: $message")
}
Button(onClick = { navController.navigate("home") }) {
Text("Take me back, please")
}
}
}
}
}