목록-세부정보 레이아웃 빌드

목록 세부정보는 하나의 창은 항목 목록을 표시하고 다른 창에는 목록에서 선택한 항목의 세부정보를 표시하는 이중 창 레이아웃으로 구성된 UI 패턴입니다.

이 패턴은 이메일 목록과 각 이메일 메시지의 세부 콘텐츠가 있는 이메일 클라이언트와 같이 대규모 컬렉션 요소에 대한 심층 정보를 제공하는 애플리케이션에 특히 유용합니다. 목록-세부정보는 앱 환경설정을 세부정보 창에 각 카테고리의 환경설정이 있는 카테고리 목록으로 분할하는 등 중요도가 낮은 경로에도 사용할 수 있습니다.

ListDetailPaneScaffold로 UI 패턴 구현

ListDetailPaneScaffold는 앱에서 목록-세부정보 패턴의 구현을 간소화하는 컴포저블입니다. 목록 세부정보 스캐폴드는 목록 창, 세부정보 창, 선택적 추가 창 등 최대 세 개의 창으로 구성될 수 있습니다. Scaffold는 화면 공간 계산을 처리합니다. 충분한 화면 크기가 있으면 목록 창과 함께 세부정보 창이 표시됩니다. 작은 화면 크기에서는 Scaffold가 자동으로 목록 창 또는 세부정보 창 전체 화면을 표시하도록 전환됩니다.

목록 페이지와 함께 표시되는 세부정보 창
그림 1. 충분한 화면 크기가 있으면 목록 창과 함께 세부정보 창이 표시됩니다.
항목을 선택하면 세부정보 창에 전체 화면이 표시됩니다.
그림 2. 화면 크기가 제한되면 (항목이 선택된 상태이므로) 세부정보 창이 전체 공간을 차지합니다.

종속 항목 선언

ListDetailPaneScaffoldMaterial 3 적응형 라이브러리의 일부입니다. 다음과 같이 앱 또는 모듈의 build.gradle 파일에 라이브러리의 종속 항목을 추가합니다.

implementation("androidx.compose.material3.adaptive:adaptive:1.0.0-alpha07")
implementation("androidx.compose.material3.adaptive:adaptive-layout:1.0.0-alpha07")
implementation("androidx.compose.material3.adaptive:adaptive-navigation:1.0.0-alpha07")

기본 사용법

다음은 ListDetailPaneScaffold의 기본 사용법을 보여줍니다.

  1. 목록에서 현재 선택된 항목을 변경 가능한 상태 변수에 저장합니다. 이 변수는 세부정보 창에 표시할 항목을 보유합니다. 일반적으로 아직 선택되지 않았음을 나타내는 null를 사용하여 현재 선택된 항목을 초기화하는 것이 좋습니다.

    class MyItem(val id: Int) {
        companion object {
            val Saver: Saver<MyItem?, Int> = Saver(
                { it?.id },
                ::MyItem,
            )
        }
    }

    var selectedItem: MyItem? by rememberSaveable(stateSaver = MyItem.Saver) {
        mutableStateOf(null)
    }

  2. rememberListDetailPaneScaffoldNavigatorThreePaneScaffoldNavigator를 만들고 BackHandler를 추가합니다. 이 탐색기는 목록, 세부정보, 추가 창 간에 이동하고 상태를 Scaffold에 제공하는 데 사용됩니다. 추가된 BackHandler은 시스템 뒤로 동작 또는 버튼을 사용하여 뒤로 이동하는 기능을 지원합니다. ListDetailPaneScaffold의 뒤로 버튼에 예상되는 동작은 창 크기와 현재 Scaffold 값에 따라 다릅니다. ListDetailPaneScaffold에서 현재 상태로 돌아갈 수 있는 경우 canNavigateBack()true가 되어 BackHandler를 사용 설정합니다.

    val navigator = rememberListDetailPaneScaffoldNavigator<Nothing>()
    
    BackHandler(navigator.canNavigateBack()) {
        navigator.navigateBack()
    }

  3. 만든 navigatorscaffoldStateListDetailPaneScaffold 컴포저블에 전달합니다.

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        // ...
    )

  4. ListDetailPaneScaffold에 목록 창 구현을 제공합니다. 새로 선택한 항목을 캡처하기 위한 콜백 인수가 구현에 포함되어 있는지 확인합니다. 이 콜백이 트리거되면 selectedItem 상태 변수를 업데이트하고 ThreePaneScaffoldNavigator를 사용하여 세부정보 창 (ListDetailPaneScaffoldRole.Detail)을 표시합니다.

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane = {
            AnimatedPane(Modifier) {
                MyList(
                    onItemClick = { id ->
                        // Set current item
                        selectedItem = id
                        // Switch focus to detail pane
                        navigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
                    }
                )
            }
        },
        // ...
    )

  5. ListDetailPaneScaffold에 세부정보 창 구현을 포함합니다. selectedItem가 null이 아닌 경우에만 세부정보 콘텐츠를 표시합니다.

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane =
        // ...
        detailPane = {
            AnimatedPane(Modifier) {
                selectedItem?.let { item ->
                    MyDetails(item)
                }
            }
        },
    )

위 단계를 구현하면 코드가 다음과 같아야 합니다.

// Currently selected item
var selectedItem: MyItem? by rememberSaveable(stateSaver = MyItem.Saver) {
    mutableStateOf(null)
}

// Create the ListDetailPaneScaffoldState
val navigator = rememberListDetailPaneScaffoldNavigator<Nothing>()

BackHandler(navigator.canNavigateBack()) {
    navigator.navigateBack()
}

ListDetailPaneScaffold(
    directive = navigator.scaffoldDirective,
    value = navigator.scaffoldValue,
    listPane = {
        AnimatedPane(Modifier) {
            MyList(
                onItemClick = { id ->
                    // Set current item
                    selectedItem = id
                    // Display the detail pane
                    navigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
                },
            )
        }
    },
    detailPane = {
        AnimatedPane(Modifier) {
            // Show the detail pane content if selected item is available
            selectedItem?.let { item ->
                MyDetails(item)
            }
        }
    },
)