{"id":26184647,"url":"https://github.com/tkuenneth/compose_adaptive_scaffold","last_synced_at":"2025-03-11T22:59:56.328Z","repository":{"id":152851130,"uuid":"624879532","full_name":"tkuenneth/compose_adaptive_scaffold","owner":"tkuenneth","description":"Make supporting large screens and foldables a breeze","archived":false,"fork":false,"pushed_at":"2024-09-17T08:16:03.000Z","size":397,"stargazers_count":95,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-09-17T15:22:40.909Z","etag":null,"topics":["android","foldablelayout","kotlin"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tkuenneth.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-07T13:40:43.000Z","updated_at":"2024-09-17T08:16:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"72bbd31e-389c-4d05-af2c-7b70c3d6f131","html_url":"https://github.com/tkuenneth/compose_adaptive_scaffold","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkuenneth%2Fcompose_adaptive_scaffold","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkuenneth%2Fcompose_adaptive_scaffold/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkuenneth%2Fcompose_adaptive_scaffold/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkuenneth%2Fcompose_adaptive_scaffold/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tkuenneth","download_url":"https://codeload.github.com/tkuenneth/compose_adaptive_scaffold/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243125544,"owners_count":20240276,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","foldablelayout","kotlin"],"created_at":"2025-03-11T22:59:55.945Z","updated_at":"2025-03-11T22:59:56.320Z","avatar_url":"https://github.com/tkuenneth.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Welcome to compose_adaptive_scaffold\n\nThe aim of this library is to make writing Jetpack Compose apps that\nsupport large screen and foldable devices a breeze. Here's a short clip that showcases how the\nlibrary works. Clicking on the preview image directs you to YouTube.\n\n[![Watch the video](https://img.youtube.com/vi/nJDmJ0mmpys/mqdefault.jpg)](https://youtu.be/nJDmJ0mmpys)\n\n*compose_adaptive_scaffold* is based on the idea of two panes, called *body* and *secondary body*.\nFor small screens you pass alternatives (or variations) called *small body* and *small secondary\nbody* (the latter one is optional). Depending on your screen layout, the pairs *body* and *small\nbody*, and *secondary body* and *small secondary body* may even be the same. Two panes are the basis\nfor *Canonical Layouts*, an important Material Design concept.\n\nUnder the hood, *compose_adaptive_scaffold* uses *Jetpack WindowManager* to provide full hinge \nsupport. This means that you do not need to worry about screen dimensions, location and sizes of \nhinges, and hinge features like orientation. Just provide *body* and *secondary body* \ncomposables - everything else is handled by *compose_adaptive_scaffold*.\n\n\u003cp\u003e\n\u003cimg src=\"./docs/Duo_01.jpg\" width=\"15%\" valign=\"top\" alt=\"Picture of Microsoft Surface Duo portrait folded\" /\u003e\n\u003cimg src=\"./docs/Duo_02.jpg\" width=\"22%\" valign=\"top\" alt=\"Picture of Microsoft Surface Duo landscape folded\" /\u003e\n\u003cimg src=\"./docs/Duo_03.jpg\" width=\"30%\" valign=\"top\" alt=\"Picture of Microsoft Surface Duo portrait opened\" /\u003e\n\u003cimg src=\"./docs/Duo_04.jpg\" width=\"25%\" valign=\"top\" alt=\"Picture of Microsoft Surface Duo landscape opened\" /\u003e\n\u003c/p\u003e\n\n### Setup\n\nThe library is available through Maven Central. To include it in your apps, just add an\nimplementation dependency:\n\n```groovy\ndependencies {\n    implementation \"com.github.tkuenneth:compose_adaptive_scaffold:0.5.2\"\n}\n```\n\nIt uses the following configuration:\n\n| Property | Value                                |\n| -------- |--------------------------------------|\n| `namespace` | `eu.thomaskuenneth.adaptivescaffold` |\n| `minSdk` | `30`                                 |\n| `targetSdk` | `35`                                 |\n\nUsed libraries:\n\n| Name | Version      |\n| -------- |--------------|\n| *Jetpack WindowManager* | `1.3.0`      |\n| *Jetpack Compose BOM* | `2024.09.01` |\n\n### How to use\n\nHere's how a simple sample activity looks like:\n\n```kotlin\nclass SimpleDemoActivity : ComponentActivity() {\n  @OptIn(ExperimentalMaterial3Api::class)\n  override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n    setContentRepeatOnLifecycleStarted {\n      MaterialTheme(\n        content = {\n          AdaptiveScaffold(\n            startDestination = destination1,\n            otherDestinations = listOf(destination2),\n            onDestinationChanged = {\n              // do something\n            },\n            topBar = {\n              TopAppBar(\n                title = {\n                  Text(\n                    text = stringResource(\n                      id = R.string.app_name\n                    )\n                  )\n                })\n            },\n          )\n        },\n        colorScheme = defaultColorScheme()\n      )\n    }\n  }\n}\n```\n\nHave you noticed there is no `setContent {}` but instead `setContentRepeatOnLifecycleStarted {}` is used?\nThis is just a tiny wrapper. It is implemented like this:\n\n```kotlin\nfun ComponentActivity.setContentRepeatOnLifecycleStarted(\n    enableEdgeToEdge: Boolean = false,\n    parent: CompositionContext? = null,\n    content: @Composable () -\u003e Unit\n) {\n    if (enableEdgeToEdge) enableEdgeToEdge()\n    lifecycleScope.launch {\n        lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {\n            setContent(\n                parent = parent,\n                content = content\n            )\n        }\n    }\n}\n```\n\nThis is needed to get informed upon posture and orientation changes. The two destinations that are passed\nto `AdaptiveScaffold` are defined like this:\n\n```kotlin\nval destination1 = NavigationDestination(\n    icon = R.drawable.ic_android_black_24dp,\n    label = R.string.one,\n    body = {\n        Box(\n            modifier = Modifier\n                .fillMaxSize()\n                .background(color = Color.Red)\n        )\n    },\n    secondaryBody = {\n        Box(\n            modifier = Modifier\n                .fillMaxSize()\n                .background(color = Color.Green)\n        )\n    },\n    smallBody = {\n        Box(\n            modifier = Modifier\n                .fillMaxSize()\n                .background(color = Color.Blue)\n        )\n    },\n    smallSecondaryBody = {\n        Box(\n            modifier = Modifier\n                .fillMaxSize()\n                .background(color = Color.Yellow)\n        )\n    },\n)\n\nval destination2 = NavigationDestination(\n    icon = R.drawable.ic_android_black_24dp,\n    label = R.string.two,\n    overlay = {\n        Box(\n            modifier = Modifier\n                .fillMaxSize()\n                .background(color = Color.LightGray)\n        )\n    },\n)\n```\n\n`NavigationDestination` receives an icon, a label, and five composable functions:\n\n1. a body\n2. a secondary body\n3. a small body\n4. a small secondary body (can be `null`)\n5. an overlay (optional)\n\nDepending on the app window size, either *body* and *secondary body* **or** *small body*\nand *small secondary body* are shown. If the device has a hinge, all features of the hinge including\nits location, size, and orientation are honored.\n\nIf you want to show something that should span the two panes, you can pass a `overlay` composable \nto `NavigationDestination`. Please have a look at `destinationFoldInfo` in *AdaptiveScaffoldDemoActivity.kt*\nto see how that works. In this example the two panes are empty, so the overlay becomes the main content.\n\nInside your composable functions, you can use `LocalFoldDef.current` to find out the \ncurrent window size classes and the configuration of the fold or hinge.\n\n```kotlin\n@Composable\nprivate fun FoldDefInfo() {\n  with(LocalFoldDef.current) {\n    LazyColumn(\n      contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),\n    ) {\n      item {\n        FoldDefInfoItem(\"isSeparating\", isSeparating.toString())\n        FoldDefInfoItem(\"orientation\", orientation.toString())\n        FoldDefInfoItem(\"occlusionType\", occlusionType.toString())\n        VerticalSpacer()\n        FoldDefInfoItem(\n          \"windowWidthSizeClass\",\n          windowSizeClass.windowWidthSizeClass.toString().lastPart()\n        )\n        FoldDefInfoItem(\n          \"windowHeightSizeClass\",\n          windowSizeClass.windowHeightSizeClass.toString().lastPart()\n        )\n        VerticalSpacer()\n        FoldDefInfoItem(\"foldWidth\", foldWidth.toString())\n        FoldDefInfoItem(\"foldHeight\", foldHeight.toString())\n        VerticalSpacer()\n        FoldDefInfoItem(\"widthLeftOrTop\", widthLeftOrTop.toString())\n        FoldDefInfoItem(\"heightLeftOrTop\", heightLeftOrTop.toString())\n        VerticalSpacer()\n        FoldDefInfoItem(\"widthRightOrBottom\", widthRightOrBottom.toString())\n        FoldDefInfoItem(\"heightRightOrBottom\", heightRightOrBottom.toString())\n      }\n    }\n  }\n}\n```\n\n### Acknowledgements\n\n*compose_adaptive_scaffold* is inspired by the Flutter \npackage [flutter_adaptive_scaffold](https://pub.dev/packages/flutter_adaptive_scaffold).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftkuenneth%2Fcompose_adaptive_scaffold","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftkuenneth%2Fcompose_adaptive_scaffold","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftkuenneth%2Fcompose_adaptive_scaffold/lists"}