{"id":19521787,"url":"https://github.com/smarttoolfactory/compose-extended-gestures","last_synced_at":"2025-08-16T18:33:41.469Z","repository":{"id":41501552,"uuid":"480060608","full_name":"SmartToolFactory/Compose-Extended-Gestures","owner":"SmartToolFactory","description":"Counterpart of onTouchEvent, TouchDelegate, Transform gestures that notifies start, end, main pointer, pointers and option to consume PointerInputChange which defines whether other gestures should receive or not.","archived":false,"fork":false,"pushed_at":"2024-06-18T18:35:43.000Z","size":1094,"stargazers_count":97,"open_issues_count":4,"forks_count":8,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-12-08T20:36:43.916Z","etag":null,"topics":["android","android-gesture","gesture-propagation","jetpack-compose","jetpack-compose-gestures","kotlin","touchdelegate","transform-gestures"],"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/SmartToolFactory.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":"2022-04-10T15:35:32.000Z","updated_at":"2024-10-27T18:54:40.000Z","dependencies_parsed_at":"2024-06-18T23:42:23.782Z","dependency_job_id":"63b731bf-78f2-40c3-8b11-93f6764d0c70","html_url":"https://github.com/SmartToolFactory/Compose-Extended-Gestures","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmartToolFactory%2FCompose-Extended-Gestures","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmartToolFactory%2FCompose-Extended-Gestures/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmartToolFactory%2FCompose-Extended-Gestures/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmartToolFactory%2FCompose-Extended-Gestures/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SmartToolFactory","download_url":"https://codeload.github.com/SmartToolFactory/Compose-Extended-Gestures/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230051325,"owners_count":18165198,"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-gesture","gesture-propagation","jetpack-compose","jetpack-compose-gestures","kotlin","touchdelegate","transform-gestures"],"created_at":"2024-11-11T00:34:55.776Z","updated_at":"2024-12-17T02:07:08.807Z","avatar_url":"https://github.com/SmartToolFactory.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Jetpack Compose Gestures\n\n[![](https://jitpack.io/v/SmartToolFactory/Compose-Extended-Gestures.svg)](https://jitpack.io/#SmartToolFactory/Compose-Extended-Gestures)\n\nJetpack Compose gesture library that expands avaiable gesture functions with onTouchEvent counterpart of event, transform and touch delegate gestures.\n\n\nhttps://user-images.githubusercontent.com/35650605/177026717-2819c0dd-e43d-466f-bfc0-2dcad268ae40.mp4\n\n\n\n## onTouch Event for Jetpack Compose\n\n### Modifier.pointerMotionEvents\n\nCreates a modifier for processing pointer motion input within the region of the modified element.\n\nAfter `AwaitPointerEventScope.awaitFirstDown` returns a `PointerInputChange` followed by `onDown`\nat first pointer contact. Moving any pointer invokes `AwaitPointerEventScope.awaitPointerEvent`\nthen `onMove` is called. When last pointer is up `onUp` is called.\n\nTo prevent other pointer functions that call `awaitFirstDown`\nor `awaitPointerEvent` (scroll, swipe, detect functions)\ncall `PointerInputChange.consume()` in `onDown`, and call `PointerInputChange.consume()` in `onMove`\nblock.\n\n```kotlin\nfun Modifier.pointerMotionEvents(\n    vararg keys: Any?,\n    onDown: (PointerInputChange) -\u003e Unit = {},\n    onMove: (PointerInputChange) -\u003e Unit = {},\n    onUp: (PointerInputChange) -\u003e Unit = {},\n    delayAfterDownInMillis: Long = 0L\n) = this.then(\n    Modifier.pointerInput(keys) {\n        detectMotionEvents(onDown, onMove, onUp, delayAfterDownInMillis)\n    }\n)\n```\n\n### Modifier.pointerMotionEventList\n\npointerMotionEventList returns list of pointers in `onMove`\n\n```kotlin\nfun Modifier.pointerMotionEventList(\n    key1: Any? = Unit,\n    onDown: (PointerInputChange) -\u003e Unit = {},\n    onMove: (List\u003cPointerInputChange\u003e) -\u003e Unit = {},\n    onUp: (PointerInputChange) -\u003e Unit = {},\n    delayAfterDownInMillis: Long = 0L\n) \n```\n\n* `delayAfterDownInMillis` parameter invokes Coroutines delay between `onDown`, and `onMove`. There\n  is a delay about 20ms between in View's `onTouchEvent` first touch and move, similar delay might\n  be required with Compose too, especially when drawing to `Canvas` which misses very fast events,\n  Delaying move behavior might be required to detect whether is touch is in required region of\n  Composable at first pointer contact.\n\n`PointerInputChange` down and move events should be consumed if you need to prevent other gestures\nlike **scroll** or other **pointerInput**s to not intercept your gesture\n\n```kotlin\n Modifier.pointerMotionEvents(\n    onDown = {\n        // When down is consumed\n        it.consume()\n    },\n    onMove = {\n        // Consuming move prevents scroll other events to not get this move event\n        it.consume()\n    },\n    delayAfterDownInMillis = 20\n)\n```\n\nYou can refer [this answer](https://stackoverflow.com/a/70847531/5457853) for details.\n\n## Transform Gestures\n### detectTransformGesturesAndChanges\n\nA gesture detector for rotation, panning, and zoom. Once touch slop has been reached, the user can\nuse rotation, panning and zoom gestures. `onGesture` will be called when any of the rotation, zoom\nor pan occurs, passing the rotation angle in degrees, zoom in scale factor and pan as an offset in\npixels. Each of these changes is a difference between the previous call and the current gesture.\nThis will consume all position changes after touch slop has been reached. onGesture will also\nprovide centroid of all the pointers that are down.\n\nAfter gesture started when last pointer is up `onGestureEnd` is triggered.\n`pointerList` returns info about pointers that are available to this gesture. \n`mainPointerInputChange` is the first pointer that is down initially, if it's lifted while\nother pointers are down `mainPointer` is set the first one in `pointerList`\n\nUsage\n\n```kotlin\nModifier.pointerInput(Unit) {\n  detectTransformGestures(\n    onGestureStart = {\n      transformDetailText = \"GESTURE START\"\n    },\n    onGesture = { gestureCentroid: Offset,\n                  gesturePan: Offset,\n                  gestureZoom: Float,\n                  gestureRotate: Float,\n                  mainPointerInputChange: PointerInputChange,\n                  pointerList: List\u003cPointerInputChange\u003e -\u003e\n     \n    },\n    onGestureEnd = {\n      borderColor = Color.LightGray\n      transformDetailText = \"GESTURE END\"\n    }\n  )\n}\n```\n\n### detectPointerTransformGestures\n\nTransform gesture as `detectTransformGestures` except with `gestureEnd` callback, returns number of\npointers that are down and checks for requisite and number of pointers before continuing transform\ngestures. when requisite is not met gesture is on hold and ends when last pointer is up. This might\nbe useful in scenarios like not panning when pointer number is higher than 1, or scenarios require\nspecific conditions to be met\n\n```kotlin\nModifier\n    .pointerInput(Unit) {\n        detectPointerTransformGestures(\n            numberOfPointers = 1,\n            requisite = PointerRequisite.GreaterThan,\n          onGestureStart = {\n            transformDetailText = \"GESTURE START\"\n          },\n            onGesture = { gestureCentroid: Offset,\n                          gesturePan: Offset,\n                          gestureZoom: Float,\n                          gestureRotate: Float,\n                          numberOfPointers: Int -\u003e\n\n            },\n            onGestureEnd = {\n                transformDetailText = \"GESTURE END\"\n            }\n        )\n    }\n```\n\n## TouchDelegate for Jetpack Compose\nModifier to handle situations where you want a view to have a larger touch area than  \nits actual Composable bounds. [dpRect] increases when values are positive and  \ndecreases touch area by negative values entered for any side\n\n```\nfun Modifier.touchDelegate(\n    dpRect: DelegateRect = DelegateRect.Zero,\n    enabled: Boolean = true,\n    onClickLabel: String? = null,\n    role: Role? = null,\n    onClick: () -\u003e Unit\n) =\n    composed(\n        inspectorInfo = {\n            name = \"touchDelegate\"\n            properties[\"dpRect\"] = dpRect\n            properties[\"enabled\"] = enabled\n            properties[\"onClickLabel\"] = onClickLabel\n            properties[\"role\"] = role\n            properties[\"onClick\"] = onClick\n        },\n        factory = {\n\n            Modifier.touchDelegate(\n                dpRect = dpRect,\n                enabled = enabled,\n                onClickLabel = onClickLabel,\n                onClick = onClick,\n                role = role,\n                indication = LocalIndication.current,\n                interactionSource = remember { MutableInteractionSource() }\n            )\n        }\n    )\n```\n\nTo increase touch area of Composable without changing its dimensions and scale\nset a `DelegateRect(left,top,right,bottom)` with positive values to increase touch area on\nsides specified by rectangle or negative values to decrease touch area.\n\n## Gesture Tutorial\n\nIf you need more detailed tutorial about Jetpack Compose gestures\ncheck [this tutorial](https://github.com/SmartToolFactory/Jetpack-Compose-Tutorials#gesture)\n\n## Gradle Setup\n\nTo get a Git project into your build:\n\n* Step 1. Add the JitPack repository to your build file Add it in your root build.gradle at the end\n  of repositories:\n\n```\nallprojects {\n  repositories {\n      ...\n      maven { url 'https://jitpack.io' }\n  }\n}\n```\n\n* Step 2. Add the dependency\n\n```\ndependencies {\n  implementation 'com.github.SmartToolFactory:Compose-Extended-Gestures:Tag'\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmarttoolfactory%2Fcompose-extended-gestures","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmarttoolfactory%2Fcompose-extended-gestures","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmarttoolfactory%2Fcompose-extended-gestures/lists"}