💡 Compose Stateless의 장점
- UI 재사용성
- UI 테스트 가능성 있음
💡 Compose를 Stateful할 때는?
- 재사용성은 줄어듦
- 테스트 가능성 사라짐
MainActivity에 다음 코드 추가
@Composable
fun OnboardingScreen(modifier: Modifier = Modifier) {
// by : = 대신 사용, 매번 .value를 입력할 필요가 없도록 해주는 속성
var shouldShowOnboarding by remember { mutableStateOf(true) }
Column(
modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Welcome to the Basics Codelab!")
Button(
modifier = Modifier.padding(vertical = 24.dp),
onClick = { shouldShowOnboarding = false }
) {
Text("Continue")
}
}
}
@Preview(showBackground = true, widthDp = 320, heightDp = 320)
@Composable
fun OnboardingPreview() {
JetpackCompose_StudyTheme {
OnboardingScreen()
}
}
여기서 OnboardingScreen에서 만든 상태를 MyApp 컴포저블과 공유해야 상태에 엑세스 할 수 있다.
이는 상태 값에 액세스해야 하는 공통 상위 요소로 상태 값을 이동하는 것이다.
먼저 MyApp의 콘텐츠를 Greetings라는 새 컴포저블을 생성해 이동한다.
미리보기에서도 Greetings 메서드를 호출하도록 수정한다.
@Composable
fun MyApp(modifier: Modifier = Modifier) {
Greetings()
}
@Composable
private fun Greetings(
modifier: Modifier = Modifier,
names: List<String> = listOf("World", "Compose")
) {
Column(modifier = modifier.padding(vertical = 4.dp)) {
for (name in names) {
Greeting(name = name)
}
}
}
@Preview(showBackground = true, widthDp = 320)
@Composable
fun GreetingsPreview() {
BasicsCodelabTheme {
Greetings()
}
}
최상위 MyApp Composable Preview를 추가
@Preview
@Composable
fun MyAppPreview() {
JetpackCompose_StudyTheme {
MyApp(Modifier.fillMaxSize())
}
}

MyApp에 OnboardingScreen외에도 다른 화면을 보여주는 로직 추가 구현한다.
@Composable
fun MyApp(
modifier: Modifier = Modifier, // 빈 수정자가 할당되는 수정자 매개변수를 포함
) {
// by : = 대신 사용, 매번 .value를 입력할 필요가 없도록 해주는 속성
var shouldShowOnboarding by remember { mutableStateOf(true) }
Surface(modifier) {
if (shouldShowOnboarding) { // onBoarding이 보여질 때 (true일 때)
// OnboardingScreen 실행해 Button이 클릭되면 false로 바꿔주기
OnboardingScreen(onContinueClicked = {shouldShowOnboarding = false})
} else { // onBoarding이 보여지지 않을 때 (false일 때)
Greetings() // 다른 화면 출력
}
}
}
@Composable
fun OnboardingScreen(
modifier: Modifier = Modifier,
onContinueClicked: () -> Unit
) {
Column(
modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Welcome to the Basics Codelab!")
Button(
modifier = Modifier.padding(vertical = 24.dp),
onClick = onContinueClicked
) {
Text("Continue")
}
}
}
@Preview(showBackground = true, widthDp = 320, heightDp = 320)
@Composable
fun OnboardingPreview() {
JetpackCompose_StudyTheme {
OnboardingScreen(onContinueClicked = {})
}
}
위와 같은 방식은 상태가 아닌 함수를 OnboardingScreen에 전달하는 방식이다.
장점은 다음과 같다.

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ElevatedButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.jetpackcompose_study.ui.theme.JetpackCompose_StudyTheme
import androidx.compose.runtime.remember
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.material3.Button
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { // 레이아웃 정의
JetpackCompose_StudyTheme {
MyApp(modifier = Modifier.fillMaxSize())
}
}
}
}
@Composable
fun MyApp(
modifier: Modifier = Modifier, // 빈 수정자가 할당되는 수정자 매개변수를 포함
) {
// by : = 대신 사용, 매번 .value를 입력할 필요가 없도록 해주는 속성
var shouldShowOnboarding by remember { mutableStateOf(true) }
Surface(modifier) {
if (shouldShowOnboarding) { // onBoarding이 보여질 때 (true일 때)
// OnboardingScreen 실행해 Button이 클릭되면 false로 바꿔주기
OnboardingScreen(onContinueClicked = {shouldShowOnboarding = false})
} else { // onBoarding이 보여지지 않을 때 (false일 때)
Greetings() // 다른 화면 출력
}
}
}
@Composable
fun OnboardingScreen(
modifier: Modifier = Modifier,
onContinueClicked: () -> Unit
) {
Column(
modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Welcome to the Basics Codelab!")
Button(
modifier = Modifier.padding(vertical = 24.dp),
onClick = onContinueClicked
) {
Text("Continue")
}
}
}
@Composable
private fun Greetings(
modifier: Modifier = Modifier,
names: List<String> = listOf("World", "Compose")
) {
Column(modifier = modifier.padding(vertical = 4.dp)) {
for (name in names) {
Greeting(name = name)
}
}
}
// 구성 가능한 함수: 함수가 내부에서 다른 @Composable 함수 호출 가능
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
var expanded by remember { mutableStateOf(false) }
val extraPadding = if (expanded) 48.dp else 0.dp
Surface(
color = MaterialTheme.colorScheme.primary,
modifier = modifier.padding(vertical = 4.dp, horizontal = 8.dp)
) {
Row(modifier = Modifier.padding(24.dp)) {
Column(
modifier = Modifier
.weight(1f)
.padding(bottom = extraPadding)
) {
Text(text = "Hello")
Text(text = name)
}
ElevatedButton(
onClick = { expanded = !expanded },
) {
Text(if (expanded) "Show less" else "Show more")
}
}
}
}
@Preview
@Composable
fun MyAppPreview() {
JetpackCompose_StudyTheme {
MyApp(Modifier.fillMaxSize())
}
}
@Preview(showBackground = true, widthDp = 320, heightDp = 320)
@Composable
fun OnboardingPreview() {
JetpackCompose_StudyTheme {
OnboardingScreen(onContinueClicked = {})
}
}
@Preview(showBackground = true, widthDp = 320)
@Composable
fun GreetingPreview() {
JetpackCompose_StudyTheme {
Greetings()
}
}