A customizable virtual joystick library for Compose Multiplatform that provides touch-based directional controls with configurable dead zones, multiple direction types, and customizable visual styles.
- Multiplatform Support: Works on Android, iOS, Desktop (JVM), Web (JS), and WASM
- Fully Customizable: Control appearance with custom drawing scopes
- Multiple Direction Types: Support for 4-direction and 8-direction control schemes
- Dead Zone Configuration: Adjustable invalid radius for precise control
- Event System: Reactive event flow with move start, moving, held, and end states
- Built-in Backgrounds: Multiple pre-built background styles including D-pad variants
- Compose-First: Built with Jetpack Compose best practices
Add the dependency to your build.gradle.kts:
commonMain.dependencies {
implementation("io.github.yoimerdr.compose:virtualjoystick:x.x.x")
}@Composable
fun SampleJoystick() {
val joystickState = rememberJoystickState()
VirtualJoystick(
state = joystickState,
modifier = Modifier.size(200.dp),
properties = CircleDrawDefaults.properties()
) { snapshot ->
// Handle joystick movement
println("Direction: ${snapshot.direction}, Strength: ${snapshot.strength}")
}
}@Composable
fun SampleJoystick() {
val joystickState = rememberJoystickState(
invalidRadius = Radius.Ratio(0.15f),
directionType = DirectionType.Complete
)
val eventHolder = rememberJoystickEventHolder(joystickState)
LaunchedEffect(eventHolder) {
eventHolder.events.collect { event ->
when (event) {
is JoystickMoveStart -> println("Started: ${event.snapshot}")
is JoystickMoving -> println("Moving: ${event.snapshot}")
is JoystickMoveHeld -> println("Held: ${event.snapshot}")
is JoystickMoveEnd -> println("Ended: ${event.snapshot}")
}
}
}
VirtualJoystick(
state = joystickState,
modifier = Modifier.size(200.dp),
properties = CircleDrawDefaults.properties(),
holder = eventHolder
)
}@Composable
fun CustomStyledJoystick() {
val state = rememberJoystickState()
BaseVirtualJoystick(
state = state,
modifier = Modifier.size(200.dp),
onMove = {
}
) {
// Custom background
Canvas(
modifier = Modifier.fillMaxSize()
) {
drawCircle(
brush = SolidColor(Color.Gray.copy(alpha = 0.3f)),
radius = state.radius
)
}
// Custom indicator
JoystickCanvas {
drawCircle(
properties = CircleDrawDefaults.properties(
brush = SolidColor(Color.Yellow),
radius = Radius.Fixed(state.radius * 0.3f),
)
)
}
}
}DirectionType.Complete: Returns all 8 directions (Right, DownRight, Down, DownLeft, Left, UpLeft, Up, UpRight, None)DirectionType.Simple: Returns only 4 cardinal directions (Right, Down, Left, Up, None)
The library includes several pre-built background styles:
BackgroundType.Default: The default background styleBackgroundType.Modern: Modern styled backgroundBackgroundType.Classic: Standard circular backgroundBackgroundType.DpadModern: Modern D-pad styleBackgroundType.DpadClassic: Classic D-pad styleBackgroundType.DpadStandard: Standard D-pad style
The JoystickState provides:
offset: Current joystick offset positiondirection: Current direction enum valuestrength: Movement strength (0.0 to 1.0)angle: Current angle in degreesisInvalid: Whether the joystick is in the dead zonegoto: Method for move the joystick to the specified position (or direction when use extension)
JoystickMoveStart: Triggered when touch beginsJoystickMoving: Triggered during movementJoystickMoveHeld: Triggered when held in positionJoystickMoveEnd: Triggered when touch ends
The composeApp module contains a comprehensive demo application showcasing:
- Different drawing types (Circle, Arc, Wedge, etc.)
- Customizable colors and styles
- Event handling examples
- Direction type switching
- Real-time state visualization
Run the sample:
./gradlew :composeApp:run| Platform | Status |
|---|---|
| Android | API 21+ |
| iOS | iOS 13+ * |
| Desktop | JVM 11+ |
| Web (JS) | Browser |
| WASM | Browser |
iOS Note: The iOS 13+ minimum version is inherited from the default Kotlin Multiplatform configuration. The library has not been fully tested on all iOS versions, so compatibility with iOS 13+ is not 100% guaranteed. If you encounter any issues on specific iOS versions, please report them in the issue tracker.
# Clone the repository
git clone https://github.com/yoimerdr/compose-virtualjoystick-multiplatform.git
# Build the library
./gradlew :library:build
# Run tests
./gradlew :library:test
# Publish to Maven Local
./gradlew :library:publishToMavenLocal- Kotlin 2.2.21+
- Compose Multiplatform 1.9.3+
- Android minSdk 21+
- iOS 13+
- JVM 11+
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
- Fork the repository
- Create your feature branch
- Commit your changes
- Push to the branch
- Open a Pull Request
This project is licensed under the Apache 2.0 License - see the LICENSE file for details.
- Built with Compose Multiplatform
- Published using Vanniktech Maven Publish Plugin
If you find this library useful, please consider giving it a ⭐ on GitHub!
