App 개발 일지/Nutri Capture

Nutri Capture 프론트엔드 - Scaffold

interfacer_han 2024. 9. 25. 15:59

#1 개요

#1-1 첫 삽 뜨기

무엇부터 손을 대야할 지 감이 잡히지 않는다. 프로토타이핑을 한번 더 해야 하나? 소프트웨어 모델링이라는 건 뭐지... 이걸 먼저 공부해야할까? ...  여러 의문이 든다. 그래도 분명한 건 앞으로 내가 핵심적으로 공들여야할 무언가(핵심 과제)는 분명 존재할 것이라는 사실이다. 가령 프론트엔드에서 매끄러운 애니메이션을 구현해야한다거나 생명주기를 말끔하게 처리하는 작업들이 그 예일 것이다. 그 과제까지 닿기 위해서 먼저 확실히 해두어야 하는 것들부터 먼저 처리해야한다.

 

#1-2 Scaffold

 

[Android] Jetpack Compose - Scaffold

#1 개요#1-1 Scaffold의 사전적 의미Scaffold는 비계((건설) 높은 곳에서 공사를 할 수 있도록 임시로 설치한 가설물)라는 단어로 번역된다. #1-2 Scaffold in Jetpack Compose Jetpack Compose  |  Android Developers이

kenel.tistory.com

우선 나는 프론트엔드로 Jetpack Compose를 사용할 것이고 Scaffold도 당연히 들어간다. 따라서 Jetpack Compose의 가장 기본적인 뼈대라 볼 수 있는 Scaffold부터 집어넣고 보려고 한다. 위 게시글에 기반해서 아래 코드와 같이 Scaffold를 구성했다.

 

#2 코드

#2-1 MainScreen의 Scaffold

...

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            NutricapturenewTheme {
                Scaffold(
                    modifier = Modifier.fillMaxSize(),
                    topBar = { },
                    bottomBar = { },
                    snackbarHost = { }
                ) { innerPadding ->
                    SampleContent(
                        text = "Sample Content",
                        modifier = Modifier
                            .fillMaxSize()
                            .padding(innerPadding)
                            .background(Color.LightGray)
                    )
                }
            }
        }
    }
}

@Composable
fun SampleContent(
    text: String,
    modifier: Modifier = Modifier
) {
    Box(
        modifier = modifier
    ) {
        Button(
            onClick = { },
            modifier = Modifier.align(Alignment.Center)
        ) {
            Text(text = text)
        }
    }
}

Scaffold에서 내가 사용할 요소는 topBar, bottomBar, snackbar로 예상된다.

 

#2-2 Scaffold의 topBar

Scaffold(
    ...
    topBar = {
        TopAppBar(
            title = { Text("Sample Top Bar Text") }
        )
    },
    ...
) { innerPadding ->
    ...
}

간단하게만 구현해두었다. 나중에는 TopAppBar의 다른 요소들(이 게시글의 #3-3참조)도 추가할 것이다.

 

#2-3 Scaffold의 bottomBar

Scaffold(
    ...
    bottomBar = {
        BottomAppBar(
            content = { Text("Sample Bottom Bar Text") }
        )
    },
    ...
) { innerPadding ->
    ...
}

BottomAppBar는 일단 채워넣기용으로 사용했다. 실제로는 BottomAppBar 대신 NavigationBar를 사용할 것이다. 여기에서 구현한다.

 

#2-4 Scaffold의 snackbarHost

...

class MainActivity : ComponentActivity() {
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        setContent {
            NutricapturenewTheme {
                // Snackbar를 위한 CoroutineScope와 State
                val scope = rememberCoroutineScope()
                val snackbarHostState = remember { SnackbarHostState() }

                Scaffold(
                    ...
                    snackbarHost = {
                        SnackbarHost(
                            hostState = snackbarHostState,
                            snackbar = { snackbarData ->
                                Snackbar(
                                    modifier = Modifier.padding(12.dp),
                                ) {
                                    Text(
                                        text = snackbarData.visuals.message,
                                    )
                                }
                            }
                        )
                    }
                ) { innerPadding ->
                    SampleContent(
                        ...
                        scope = scope,
                        snackbarHostState = snackbarHostState
                    )
                }
            }
        }
    }
}

@Composable
fun SampleContent(
    ...
    scope: CoroutineScope = rememberCoroutineScope(),
    snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }
) {
    Box(
        ...
    ) {
        Button(
            onClick = {
                scope.launch {
                    snackbarHostState.showSnackbar(
                        message = "Button Clicked",
                        duration = SnackbarDuration.Short
                    )
                }
            },
            ...
        ) {
            ...
        }
    }
}

사용자에게 전달한 각종 메시지를 Snackbar를 통해 보낼 가능성이 아주 높으므로, Snackbar도 만들어둔다. 관련 변수인 scope와 snackbarHostState를 인수로 받게끔 SampleContent()을 수정하고, onClick 구현에 사용한다.

 

#2-5 전체 코드

package com.example.nutri_capture_new

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.BottomAppBar
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Snackbar
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.example.nutri_capture_new.ui.theme.NutricapturenewTheme
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {
    @OptIn(ExperimentalMaterial3Api::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            NutricapturenewTheme {
                // Snackbar를 위한 CoroutineScope와 State
                val scope = rememberCoroutineScope()
                val snackbarHostState = remember { SnackbarHostState() }

                Scaffold(
                    modifier = Modifier.fillMaxSize(),
                    topBar = {
                        TopAppBar(
                            title = { Text("Sample Top Bar Text") }
                        )
                    },
                    bottomBar = {
                        BottomAppBar(
                            content = { Text("Sample Bottom Bar Text") }
                        )
                    },
                    snackbarHost = {
                        SnackbarHost(
                            hostState = snackbarHostState,
                            snackbar = { snackbarData ->
                                Snackbar(
                                    modifier = Modifier.padding(12.dp),
                                ) {
                                    Text(
                                        text = snackbarData.visuals.message,
                                    )
                                }
                            }
                        )
                    }
                ) { innerPadding ->
                    SampleContent(
                        text = "Sample Content",
                        modifier = Modifier
                            .fillMaxSize()
                            .padding(innerPadding)
                            .background(Color.LightGray),
                        scope = scope,
                        snackbarHostState = snackbarHostState
                    )
                }
            }
        }
    }
}

@Composable
fun SampleContent(
    text: String,
    modifier: Modifier = Modifier,
    scope: CoroutineScope = rememberCoroutineScope(),
    snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }
) {
    Box(
        modifier = modifier
    ) {
        Button(
            onClick = {
                scope.launch {
                    snackbarHostState.showSnackbar(
                        message = "Button Clicked",
                        duration = SnackbarDuration.Short
                    )
                }
            },
            modifier = Modifier.align(Alignment.Center)
        ) {
            Text(text = text)
        }
    }
}

 

#3 요약

빈 프로젝트에 일단 Scaffold부터 만들었다.

 

#4 완성된 앱

#4-1 스크린샷

 

#4-2 이 게시글 시점의 Commit

 

GitHub - Kanmanemone/nutri-capture-new

Contribute to Kanmanemone/nutri-capture-new development by creating an account on GitHub.

github.com

 

#4-3 본 프로젝트의 가장 최신 Commit

 

GitHub - Kanmanemone/nutri-capture-new

Contribute to Kanmanemone/nutri-capture-new development by creating an account on GitHub.

github.com