Puck

Make Composables Draggable.





Including in your project

Gradle

Add below codes to your root build.gradle file (not your module build.gradle file).

allprojects {
    repositories {
        mavenCentral()
    }
}

And add a dependency code to your module‘s build.gradle file.

dependencies {
    implementation 'com.github.Shivamdhuria:puck:Version'
}

Usage

//Currently just copy the library module to your project. haven’t uploaded on jitpack yet

        setContent {
            // this keeps parents size
            val parentSize = remember { mutableStateOf(Size.Zero) } 
            PuckTheme {
                // A surface container using the 'background' color from the theme
                Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background) {
                    Box(modifier = Modifier
                        .onGloballyPositioned { coordinates ->
                            //Set parents Size
                            parentSize.value = coordinates.size.toSize()
                        }
                        .fillMaxSize()
                    ) {
                        //Adding puck to Card Composable
                        Card(
                            modifier = Modifier
                                .puck(parentSize, behaviour = FreeForm), backgroundColor = PINK200
                        ) {
                            Column(horizontalAlignment = Alignment.CenterHorizontally) {
                                Image(
                                    painterResource(drawable.zoid),
                                    contentDescription = "",
                                    contentScale = ContentScale.Crop,
                                    modifier = Modifier
                                        .height(70.dp)
                                        .width(70.dp)
                                )
                                Text(text = "Card Composable", fontSize = 24.sp, modifier = Modifier.padding(10.dp))
                            }
                        }
                        //Adding puck to Button Composable
                        Button(onClick = { }, modifier = Modifier.puck(parentSize, behaviour = FreeForm, animationDuration = 700)) {
                            Text(text = "Button Composable", fontSize = 24.sp)
                        }
                        
                       //Adding puck to FAB Composable
                        FloatingActionButton(
                            onClick = {},
                            modifier = Modifier
                                .puck(parentSize, FreeForm, animationDuration = 700)
                                .width(90.dp)
                                .height(90.dp),
                        ) {
                            Image(
                                painterResource(drawable.pencil),
                                contentDescription = "",
                                modifier = Modifier
                                    .height(50.dp)
                                    .width(50.dp)
                            )
                        }
                    }
                }
            }

Behaviours

Currently Puck supports three behaviours

  • Freeform
  • Sticky
  • Gravity

FreeForm

Freeform makes Composable draggable anywhere in the screen. If the user drags a composable out of screen area, it is coerced within the bounds of the screen.

For adding Freeform behaviour to any composable, do –

FloatingActionButton(onClick = {},
                            modifier = Modifier
                                .puck(parentSize, behaviour = FreeForm)
                                .width(90.dp)
                                .height(90.dp)) {
                            //Image or anything
                        }

Sticky

You can also make Composables stick to the edges or corners of the screen. Simple flick the Composable in the direction you want it to stick and it will follow the trajectory and stick there. To use stick you need to pass a parameter (EDGES, CORNERS,VERTICAL EDGsE HORIZONATL EDGES). Puck will automatically make the set parameter “sticky”.

FloatingActionButton(onClick = {},
                           modifier = Modifier
                                .puck(parentSize, behaviour = Sticky(Corners), animationDuration = 500)
                                .width(90.dp)
                                .height(90.dp),
                            backgroundColor = WHITE200
                        ) {
                            Image()
                        }

Gravity

We can define gravity points with center and radius. Any composable that falls within this circle’s gravity field will “zap” towards this center.
Note: The circle isn’t actualy drawn as above while using puck. I have shown the circle only for demonstration purposes.

val circle = Circle(800f, 1800f, 500f)
FloatingActionButton(onClick = {},
                           modifier = Modifier
                                .puck(parentSize,behaviour = Gravity(circle), animationDuration = 500)
                                .width(90.dp)
                                .height(90.dp),
                            backgroundColor = WHITE200
                        ) {
                            Image()
                        }

Attributes

  • isPointsTowardsCenter – Currently this only works for Sticky(Edges) mode. Composable will always point towards center by using rotation animation. When a composable is dragged, the rotation value is set to default (0f), after the drag ends and the commposable sticks to any of the edges, the composable rotates according and points towards center.

  • animationDuration – This is time taken in milli seconds for the drag animation. The lesser the time the quicker the animation. (Note: When the behaviour is set to Freeform, a composable is draggable without any animation.)

  • focusedSizeMultiplier – When a composable is in pressed state, its size increases by this factor. You can set it to 1, if you don’t want to increase it’s size when in pressed date

  • onOffsetChanged – This returns the latest int offset of the composable’s position after the drag ends. You should ideally use it to save the latest offset in shared preferences/ datastore to use it again and position the composable when the app restarts again.

Code & Issues

If you are a developer and you wish to contribute to the app please raise an issue, discuss, fork and submit a pull request.
Follow Github Flow for collaboration!

Find this repository useful? ❤️

Support it by joining starring this repository. ⭐
And follow me on Medium, Github and Twitter

Project Maintainers

This project is founded and actively maintained by Shivam Dhuria.

License

Copyright 2021 Shivam Dhuria

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

GitHub

View Github