{"id":16648522,"url":"https://github.com/w2sv/composed","last_synced_at":"2025-03-21T16:31:05.770Z","repository":{"id":225181319,"uuid":"765250608","full_name":"w2sv/Composed","owner":"w2sv","description":" A collection of utils to facilitate development with Jetpack Compose. ","archived":false,"fork":false,"pushed_at":"2024-11-03T09:13:36.000Z","size":1339,"stargazers_count":25,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-01T07:51:15.420Z","etag":null,"topics":["android","android-development","android-library","jetpack-android","jetpack-compose","utils","utils-library"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/w2sv.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2024-02-29T15:06:48.000Z","updated_at":"2025-02-06T06:37:04.000Z","dependencies_parsed_at":"2024-03-15T11:51:25.547Z","dependency_job_id":"c1f45f50-6fb2-40f9-94f4-7a6c136152de","html_url":"https://github.com/w2sv/Composed","commit_stats":null,"previous_names":["w2sv/compose-utils","w2sv/composed"],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/w2sv%2FComposed","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/w2sv%2FComposed/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/w2sv%2FComposed/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/w2sv%2FComposed/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/w2sv","download_url":"https://codeload.github.com/w2sv/Composed/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244147327,"owners_count":20405942,"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","android-development","android-library","jetpack-android","jetpack-compose","utils","utils-library"],"created_at":"2024-10-12T09:04:38.176Z","updated_at":"2025-03-21T16:31:05.525Z","avatar_url":"https://github.com/w2sv.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eComposed\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e    \n    \u003ca href=\"https://android-arsenal.com/api?level=21\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/API-21%2B-brightgreen.svg?style=flat\" alt=\"API\"\u003e\n\u003c/a\u003e\n    \n\u003ca href=\"https://jitpack.io/#w2sv/Composed\"\u003e\n  \u003cimg src=\"https://jitpack.io/v/w2sv/Composed.svg\" alt=\"JitPack\"\u003e\n\u003c/a\u003e\n\n\u003ca href=\"https://github.com/w2sv/Compose-Utils/actions/workflows/workflow.yaml\"\u003e\n    \u003cimg src=\"https://github.com/w2sv/Compose-Utils/actions/workflows/workflow.yaml/badge.svg\" alt=\"Build\"\u003e\n\u003c/a\u003e\n\n\u003cimg src=\"https://img.shields.io/github/license/w2sv/Compose-Utils\" alt=\"GitHub License\"\u003e\n\u003c/p\u003e\n\n------\n\n\u003cp align=\"center\"\u003e\n\u003cb\u003eA collection of utils to facilitate development with Jetpack Compose.\u003c/b\u003e\n\u003c/p\u003e\n\n------\n\n# Installation\n\nMake sure you have jitpack added to your dependency resolution repositories by adding the following to your `settings.gradle.kts`:\n\n```kotlin\ndependencyResolutionManagement {\n  repositories {\n    maven(\"https://jitpack.io\")\n  }\n}\n```\n\nThen add the dependencies you lust for to your `build.gradle.kts` files:\n\n```kotlin\ndependencies {\n    // Core utils\n    implementation \"com.github.w2sv.Composed:composed:\u003cversion\u003e\"\n    // Permission utils\n    implementation \"com.github.w2sv.Composed:permissions:\u003cversion\u003e\"\n}\n```\n\n# Contents\n\n- [State Savers](#state-savers)\n- [Styled Text](#styled-text)\n- [Modifiers](#modifiers)\n- [Layout](#layout)\n- [Flow Collectors](#flow-collectors)\n- [Lifecycle Observers](#lifecycle-observers)\n- [Orientation](#orientation)\n- [Dimension Conversion](#dimension-conversion)\n- [Color Conversion](#color-conversion)\n- [Map Conversion](#map-conversion)\n- [Drawer State](#drawerstate)\n- [SnackbarHostState](#snackbarhoststate)\n- [Easing](#easing)\n- [Permission States](#permission-states)\n\n## State Savers\n\n```kotlin\n/**\n * Returns a rememberSavable state saver for Color.\n */\nfun colorSaver(): Saver\u003cColor, Int\u003e\n\n/**\n * Returns a rememberSavable state saver for an optional Color.\n */\nfun nullableColorSaver(): Saver\u003cColor?, Float\u003e\n\n/**\n * listSaver for an optional object, enabling handling of non-null instances only.\n */\nfun \u003cOriginal, Saveable\u003e nullableListSaver(\n    saveNonNull: SaverScope.(value: Original) -\u003e List\u003cSaveable\u003e,\n    restoreNonNull: (list: List\u003cSaveable\u003e) -\u003e Original?\n): Saver\u003cOriginal?, Any\u003e\n\n/**\n * mapSaver for an optional object, enabling handling of non-null instances only.\n */\nfun \u003cT\u003e nullableMapSaver(\n    saveNonNull: SaverScope.(value: T) -\u003e Map\u003cString, Any?\u003e,\n    restoreNonNull: (Map\u003cString, Any?\u003e) -\u003e T\n): Saver\u003cT, Any\u003e\n```\n\n## Styled Text\n\n```kotlin\n/**\n * Converts a HTML-styled string resource text to a remembered AnnotatedString.\n */\n@Composable\nfun rememberStyledTextResource(@StringRes id: Int, vararg formatArgs: Any): AnnotatedString\n```\n\n## Modifiers\n\n```kotlin\n/**\n * Applies modifiers depending on a condition.\n */\ninline fun Modifier.thenIf(\n    condition: Boolean,\n    onFalse: Modifier.() -\u003e Modifier = { this },\n    onTrue: Modifier.() -\u003e Modifier = { this },\n): Modifier\n```\n\n## Layout\n\n```kotlin\n/**\n * [Column] whose [elements], rendered through [makeElement], will be divided by [makeDivider]. [makeDivider] will be invoked only in between elements, that is, neither before the first, nor after the last element.\n */\n@Composable\nfun \u003cT\u003e InterElementDividedColumn(\n    elements: List\u003cT\u003e,\n    makeElement: @Composable ColumnScope.(T) -\u003e Unit,\n    modifier: Modifier = Modifier,\n    makeDivider: @Composable ColumnScope.() -\u003e Unit = { HorizontalDivider() },\n    verticalArrangement: Arrangement.Vertical = Arrangement.Top,\n    horizontalAlignment: Alignment.Horizontal = Alignment.Start,\n)\n\n/**\n * [Row] whose [elements], rendered through [makeElement], will be divided by [makeDivider]. [makeDivider] will be invoked only in between elements, that is, neither before the first, nor after the last element.\n */\n@Composable\nfun \u003cT\u003e InterElementDividedRow(\n    elements: List\u003cT\u003e,\n    makeElement: @Composable RowScope.(T) -\u003e Unit,\n    modifier: Modifier = Modifier,\n    makeDivider: @Composable RowScope.() -\u003e Unit = { VerticalDivider() },\n    horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,\n    verticalAlignment: Alignment.Vertical = Alignment.Top,\n)\n```\n\n## Flow Collectors\n\n```kotlin\n/**\n * Collects from a flow and emits values into a collector.\n */\n@Composable\nfun \u003cT\u003e CollectFromFlow(\n    flow: Flow\u003cT\u003e,\n    key1: Any? = null,\n    key2: Any? = null,\n    collector: FlowCollector\u003cT\u003e\n)\n\n/**\n * Collects latest from a flow with given action.\n */\n@Composable\nfun \u003cT\u003e CollectLatestFromFlow(\n    flow: Flow\u003cT\u003e,\n    key1: Any? = null,\n    key2: Any? = null,\n    action: suspend (value: T) -\u003e Unit\n)\n```\n\n## Lifecycle Observers\n\n```kotlin\n/**\n * Runs a callback whenever the lifecycleOwner reaches the given lifecycleEvent.\n */\n@Composable\nfun OnLifecycleEvent(\n    lifecycleEvent: Lifecycle.Event,\n    lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,\n    key1: Any? = null,\n    key2: Any? = null,\n    callback: () -\u003e Unit\n)\n\n/**\n * Runs a callback when removed from composition.\n */\n@Composable\nfun OnDispose(callback: () -\u003e Unit)\n```\n\n## Orientation\n\n```kotlin\n/**\n * Returns true if the landscape mode is active, false otherwise.\n */\nval isLandscapeModeActive: Boolean\n\n/**\n * Returns true if the portrait mode is active, false otherwise.\n */\nval isPortraitModeActive: Boolean\n```\n\n## Dimension Conversion\n\n```kotlin\n/**\n * Converts Dp to pixels.\n */\n@Composable\n@ReadOnlyComposable\nfun Dp.toPx(): Float\n\n/**\n * Converts pixels to Dp.\n */\n@Composable\n@ReadOnlyComposable\nfun Int.toDp(): Dp\n\n/**\n * Converts pixels to Dp.\n */\n@Composable\n@ReadOnlyComposable\nfun Float.toDp(): Dp\n```\n\n## Color Conversion\n\n```kotlin\n/**\n * Converts a hex color string to Color.\n */\nfun String.toComposeColor(): Color\n```\n\n## Map Conversion\n\n```kotlin\n/**\n * Converts a regular Map to a SnapshotStateMap.\n */\nfun \u003cK, V\u003e Map\u003cK, V\u003e.toMutableStateMap(): SnapshotStateMap\u003cK, V\u003e\n```\n\n## DrawerState\n\n```kotlin\n/**\n * Returns a State\u003cFloat\u003e whose value ranges from 0.0 (drawer closed) to 1.0 (drawer fully open).\n */\nfun DrawerState.visibilityPercentage(@FloatRange(from = 0.0) maxWidthPx: Float): State\u003cFloat\u003e\n\n/**\n * Remembers a visibility percentage for the drawer.\n */\n@Composable\nfun DrawerState.rememberVisibilityPercentage(@FloatRange(from = 0.0) maxWidthPx: Float = DrawerDefaults.MaximumDrawerWidth.toPx()): State\u003cFloat\u003e\n```\n\n## SnackbarHostState\n\n```kotlin\n/**\n * Dismisses the currently showing snackbar if there is one and shows a new one with the given [snackbarVisuals].\n */\nsuspend fun SnackbarHostState.dismissCurrentSnackbarAndShow(snackbarVisuals: SnackbarVisuals)\n\n/**\n * Dismisses the currently showing snackbar if there is one and shows a new one with the given parameters.\n */\nsuspend fun SnackbarHostState.dismissCurrentSnackbarAndShow(\n    message: String,\n    actionLabel: String? = null,\n    withDismissAction: Boolean = false,\n    duration: SnackbarDuration = if (actionLabel == null) SnackbarDuration.Short else SnackbarDuration.Indefinite\n)\n```\n\n## Easing\n\n```kotlin\nfun TimeInterpolator.toEasing() = Easing\n```\n\n## Permission States\n\n```kotlin\n/**\n * Permission state which, as opposed to the accompanist ones,\n * - exposes a [grantedFromRequest] shared flow to allow for distributed subscription and callback invocation, instead of only being able to pass a onPermissionResult callback upon instantiation, which needs to cover all granting reactions, possibly impacting various components\n * - allows for callbacks upon permission requesting being suppressed\n */\n@Stable\ninterface ExtendedPermissionState {\n    val granted: Boolean\n    val grantedFromRequest: SharedFlow\u003cBoolean\u003e\n    fun launchRequest(onSuppressed: (() -\u003e Unit)? = null)\n}\n\n// With the implementations:\n\n@Stable\nopen class ExtendedSinglePermissionState(\n    private val requestLaunchedBefore: StateFlow\u003cBoolean\u003e,\n    permissionState: PermissionState,\n    override val grantedFromRequest: SharedFlow\u003cBoolean\u003e,\n    private val defaultOnLaunchingSuppressed: () -\u003e Unit = {}\n) : PermissionState by permissionState, ExtendedPermissionState\n\n@Composable\nfun rememberExtendedSinglePermissionState(\n    permission: String,\n    requestLaunchedBefore: StateFlow\u003cBoolean\u003e,\n    saveRequestLaunched: () -\u003e Unit,\n    defaultOnPermissionResult: (Boolean) -\u003e Unit = {},\n    defaultOnLaunchingSuppressed: () -\u003e Unit = {},\n    scope: CoroutineScope = rememberCoroutineScope()\n): ExtendedSinglePermissionState\n\n// And\n\n@Stable\nopen class ExtendedMultiplePermissionsState(\n    private val requestLaunchedBefore: StateFlow\u003cBoolean\u003e,\n    multiplePermissionsState: MultiplePermissionsState,\n    override val grantedFromRequest: SharedFlow\u003cBoolean\u003e,\n    private val defaultOnLaunchingSuppressed: () -\u003e Unit = {}\n) : MultiplePermissionsState by multiplePermissionsState, ExtendedPermissionState\n\n@Composable\nfun rememberExtendedMultiplePermissionsState(\n    permissions: List\u003cString\u003e,\n    requestLaunchedBefore: StateFlow\u003cBoolean\u003e,\n    saveRequestLaunched: () -\u003e Unit,\n    defaultOnPermissionResult: (Map\u003cString, Boolean\u003e) -\u003e Unit = {},\n    defaultOnLaunchingSuppressed: () -\u003e Unit = {},\n    scope: CoroutineScope = rememberCoroutineScope()\n): ExtendedMultiplePermissionsState\n```\n\n# License\n```xml\nDesigned and developed by 2024 w2sv (Janek Zangenberg)\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fw2sv%2Fcomposed","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fw2sv%2Fcomposed","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fw2sv%2Fcomposed/lists"}