https://github.com/luiisca/floating-views
Kotlin library for effortlessly creating customizable floating UI elements in Android apps.
https://github.com/luiisca/floating-views
android-library floating jetpack-compose kotlin-android
Last synced: over 1 year ago
JSON representation
Kotlin library for effortlessly creating customizable floating UI elements in Android apps.
- Host: GitHub
- URL: https://github.com/luiisca/floating-views
- Owner: luiisca
- License: mit
- Created: 2024-06-13T03:30:43.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2024-09-04T23:10:28.000Z (almost 2 years ago)
- Last Synced: 2024-09-07T06:28:41.040Z (almost 2 years ago)
- Topics: android-library, floating, jetpack-compose, kotlin-android
- Language: Kotlin
- Homepage: https://central.sonatype.com/artifact/io.github.luiisca/floating.views
- Size: 1.31 MB
- Stars: 4
- Watchers: 2
- Forks: 0
- Open Issues: 14
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
[](https://github.com/luiisca/floating-views/releases/latest)
[](https://opensource.org/licenses/MIT)
# 🎈 Floating Views
A Kotlin library for effortlessly creating customizable floating UI elements in Android apps.
## Features
- 🚀 Declarative API using Jetpack Compose for easy integration
- 🌊 Smooth animations with customizable transition specs
- 🧲 Smart edge-snapping behavior for improved UX
- 🎛️ Fine-grained control over float behavior and interactions
- 📏 Adaptive sizing to fit content and screen dimensions
- 🔒 Built-in runtime permission handling for overlay views
- 🎨 Flexible view creation with Jetpack Compose or traditional Views
- 🔌 Easy service start/stop controls from any part of your app
## Requirements
- Android SDK version 21+
- Maven central
## Quick Start
### 1. Import to project
Add the following dependency to your app's `build.gradle.kts` file:
```kotlin
dependencies {
implementation("io.github.luiisca.floating.views:1.0.5")
}
```
> [sample/build.gradle.kts:55](https://github.com/luiisca/floating-views/blob/a2a3cc587f5c8eca2b4c80f9aa78b587fab5dd86/sample/build.gradle.kts#L55)
### 2. Update your AndroidManifest.xml
Add the following to your `AndroidManifest.xml`:
```xml
```
> [sample/.../AndroidManifest.xml](https://github.com/luiisca/floating-views/blob/a2a3cc587f5c8eca2b4c80f9aa78b587fab5dd86/sample/src/main/AndroidManifest.xml)
### 3. Launch and manage your custom floating view
This library allows you to create a highly customizable floating view with three main components: main float, close float, and expanded float. Here's how to configure and launch your floating view:
3.1. **Create a custom configuration**: Use `FloatingViewsConfig` to define the behavior of a single floating view and its components.
```kotlin
val config = FloatingViewsConfig(
enableAnimations = true, // Enable or disable animations
main = MainFloatConfig(
composable = { YourMainFloatContent() },
// Configure main float behavior
),
close = CloseFloatConfig(
composable = { YourCloseFloatContent() },
// Configure close float behavior
),
expanded = ExpandedFloatConfig(
composable = { close -> YourExpandedFloatContent(close) },
// Configure expanded float behavior
)
)
```
Each config object (`MainFloatConfig`, `CloseFloatConfig`, `ExpandedFloatConfig`) allows you to customize various aspects of that specific component of your floating view.
3.2. **Launch the floating view**: Use `FloatingViewsManager.startFloatServiceIfPermitted()` to start the service and display your custom floating view.
```kotlin
FloatingViewsManager.startFloatServiceIfPermitted(context, config)
```
3.3 **Monitor service state**: Subscribe to `FloatServiceStateManager.isServiceRunning` to be notified when the service starts or stops.
```kotlin
@Composable
fun YourComposable() {
val isServiceRunning by FloatServiceStateManager.isServiceRunning.collectAsState()
// Use isServiceRunning to update your UI
if (isServiceRunning) {
Text("Service is running")
} else {
Text("Service is not running")
}
}
```
Here's an example demonstrating how to create and launch multiple, distinct floating views while monitoring the service state:
```kotlin
@Composable
fun App() {
Scaffold { innerPadding ->
println(innerPadding)
val context = LocalContext.current
val isServiceRunning by FloatServiceStateManager.isServiceRunning.collectAsState()
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
// Base Float
Button(
modifier = Modifier.widthIn(min = 200.dp, max = 300.dp),
onClick = {
val config = FloatingViewsConfig(
enableAnimations = false,
main = MainFloatConfig(
composable = { BaseFloat() },
// Add other main float configurations here
),
close = CloseFloatConfig(
closeBehavior = CloseBehavior.CLOSE_SNAPS_TO_MAIN_FLOAT,
// Add other close float configurations here
),
expanded = ExpandedFloatConfig(
composable = {close -> BaseExpandedFloat(close) },
// Add other expanded float configurations here
)
)
// Launch a new music player floating view
FloatingViewsManager.startFloatServiceIfPermitted(context, config)
}
) {
Text(text = "Base", style = MaterialTheme.typography.bodyLarge)
}
// ...
// Display a button to stop the service if it's running
if (isServiceRunning) {
Spacer(modifier = Modifier.height(16.dp))
Button(
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.error,
contentColor = MaterialTheme.colorScheme.onError
),
modifier = Modifier.widthIn(min = 200.dp, max = 300.dp),
onClick = {
FloatingViewsManager.stopFloatService(context)
}
) {
Text(text = "Remove all", style = MaterialTheme.typography.bodyLarge)
}
}
}
}
}
```
> [sample/.../App.kt](https://github.com/luiisca/floating-views/blob/a2a3cc587f5c8eca2b4c80f9aa78b587fab5dd86/sample/src/main/kotlin/com/sample/app/App.kt#L38)
## Customization
The `FloatingViewsController` offers extensive customization options. Here's an overview of key features, followed by a complete reference.
### Key Customization Examples
1. Main Floating View:
```kotlin
val mainFloatConfig = MainFloatConfig(
composable = { /* Your content */ },
startPointDp = PointF(100f, 100f),
isSnapToEdgeEnabled = true,
onTap = { /* Handle tap */ }
)
```
2. Expanded View:
```kotlin
val expandedFloatConfig = ExpandedFloatConfig(
enabled = true,
tapOutsideToClose = true,
dimAmount = 0.5f,
composable = { close -> /* Expanded content */ }
)
```
3. Close View:
```kotlin
val closeFloatConfig = CloseFloatConfig(
enabled = true,
composable = { /* Custom close button */ },
closeBehavior = CloseBehavior.MAIN_SNAPS_TO_CLOSE_FLOAT
)
```
### Complete Configuration Reference
Below is a comprehensive list of all configuration options:
#### MainFloatConfig
| Option | Description | Type | Default |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- |
| `composable` | The Composable to be rendered inside the floating view. If null, `viewFactory` is used. | `(@Composable () -> Unit)?` | `null` |
| `viewFactory` | Factory function to create a traditional Android view inside the floating view. If null, `composable` is used. | `((Context) -> View)?` | `null` |
| `startPointDp` | Initial position of the floating view in density-independent pixels (dp). When neither `startPointDp` nor `startPointPx` are provided `PointF(0,0)` is used. | `PointF?` | `null` |
| `startPointPx` | Initial position of the floating view in pixels (px). When neither `startPointDp` nor `startPointPx` are provided `PointF(0,0)` is used. | `PointF?` | `null` |
| `draggingTransitionSpec` | Animation spec for dragging transitions. Applied when `FloatingViewsController.enableAnimations` is `true`. | `(Transition.Segment.() -> FiniteAnimationSpec)` | `spring(dampingRatio = Spring.DampingRatioNoBouncy, stiffness = Spring.StiffnessHigh)` |
| `snapToEdgeTransitionSpec` | Animation spec for snapping to the screen edge. Applied when `FloatingViewsController.enableAnimations` is `true` and `isSnapToEdgeEnabled` is `true`. | `(Transition.Segment.() -> FiniteAnimationSpec)` | `spring(dampingRatio = Spring.DampingRatioMediumBouncy, stiffness = Spring.StiffnessMedium)` |
| `snapToCloseTransitionSpec` | Animation spec for snapping to close float. Applied when `FloatingViewsController.enableAnimations` is `true` and `CloseFloatConfig.closeBehavior` is `CloseBehavior.MAIN_SNAPS_TO_CLOSE_FLOAT`. | `(Transition.Segment.() -> FiniteAnimationSpec)` | `spring(dampingRatio = Spring.DampingRatioMediumBouncy, stiffness = Spring.StiffnessLow)` |
| `isSnapToEdgeEnabled` | If `true`, the floating view will snap to the nearest screen edge when dragging ends. | `Boolean` | `true` |
| `onTap` | Callback triggered when the floating view is tapped. | `((Offset) -> Unit)?` | `null` |
| `onDragStart` | Callback triggered when dragging of the floating view begins. | `((offset: Offset) -> Unit)?` | `null` |
| `onDrag` | Callback triggered during dragging of the floating view. | `((PointerInputChange, dragAmount: Offset, newPoint: Point, newAnimatedPoint: Point?) -> Unit)?` | `null` |
| `onDragEnd` | Callback triggered when dragging of the floating view ends. | `(() -> Unit)?` | `null` |
#### ExpandedFloatConfig
| Option | Description | Type | Default |
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- | ------- |
| `enabled` | If `true`, enables expanded view mode. | `Boolean` | `true` |
| `tapOutsideToClose` | If `true` adds an overlay view that will close expanded view when tapped. | `Boolean` | `true` |
| `dimAmount` | Controls the dimming amount of the background when the view is expanded. Range is from 1.0 for completely opaque to 0.0 for no dim. | `Float` | `0.5f` |
| `composable` | The Composable to be rendered inside the expanded view. If null, `viewFactory` is used. Call close to remove expanded view. | `(@Composable (close: () -> Unit) -> Unit)?` | `null` |
| `viewFactory` | Factory function to create a traditional Android view inside the expanded view. If null, `composable` is used. Call close to remove expanded view. | `((context: Context, close:() -> Unit) -> View)?` | `null` |
| All properties from MainFloatConfig | All properties from `MainFloatConfig` are also available here. | | |
#### CloseFloatConfig
| Option | Description | Type | Default |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------- |
| `enabled` | If `true`, enables the close float behavior. | `Boolean` | `true` |
| `composable` | The Composable to be rendered inside the close floating view. If null, `viewFactory` is used. | `(@Composable () -> Unit)?` | `null` |
| `viewFactory` | Factory function to create a traditional Android view inside the close floating view. If null, `composable` is used. | `((Context) -> View)?` | `null` |
| `startPointDp` | Initial position of the close floating view in density-independent pixels (dp). When neither `startPointDp` nor `startPointPx` are provided `PointF(0,0)` is used. | `PointF?` | `null` |
| `startPointPx` | Initial position of the close float in pixels (px). When neither `startPointDp` nor `startPointPx` are provided `PointF(0,0)` is used. | `PointF?` | `null` |
| `mountThresholdDp` | Dragging distance required to show the close float, in density-independent pixels (dp). When neither `mountThresholdDp` nor `mountThresholdPx` are provided `1.dp` is used. | `Float?` | `null` |
| `mountThresholdPx` | Dragging distance required to show the close float, in pixels (px). When neither `mountThresholdDp` nor `mountThresholdPx` are provided `1. | `Float?` | `null` |
| `closingThresholdDp` | Dragging distance (in density-independent pixels) between the main float and the close float that triggers a `CloseFloatConfig.closeBehavior`. When neither `closingThresholdDp` nor `closingThresholdPx` are provided `100.dp` is used. | `Float?` | `null` |
| `closingThresholdPx` | Dragging distance (in pixels) between the main float and the close float that triggers a `CloseFloatConfig.closeBehavior`. When neither `closingThresholdDp` nor `closingThresholdPx` are provided `100.dp` is used. | `Float?` | `null` |
| `bottomPaddingDp` | Bottom padding for the close float in density-independent pixels (dp). When neither `bottomPaddingDp` nor `bottomPaddingPx` are provided `16.dp` is used. | `Float?` | `null` |
| `bottomPaddingPx` | Bottom padding for the close float in pixels (px). When neither `bottomPaddingDp` nor `bottomPaddingPx` are provided `16.dp` is used. | `Float?` | `null` |
| `draggingTransitionSpec` | Animation specification for dragging. Applied when `FloatingViewsController.enableAnimations` is `true` and `CloseFloatConfig.closeBehavior` is `CloseBehavior.CLOSE_SNAPS_TO_MAIN_FLOAT`. | `(Transition.Segment.() -> FiniteAnimationSpec)` | `spring(dampingRatio = Spring.DampingRatioNoBouncy, stiffness = Spring.StiffnessHigh)` |
| `snapToMainTransitionSpec` | Animation specification for snapping to main float. Applied when `FloatingViewsController.enableAnimations` is `true` and `CloseFloatConfig.closeBehavior` is `CloseBehavior.CLOSE_SNAPS_TO_MAIN_FLOAT`. | `(Transition.Segment.() -> FiniteAnimationSpec)` | `spring(dampingRatio = Spring.DampingRatioLowBouncy, stiffness = Spring.StiffnessLow)` |
| `closeBehavior` | Determines how the close float interacts with the main float. | `CloseBehavior?` | `CloseBehavior.MAIN_SNAPS_TO_CLOSE_FLOAT` |
| `followRate` | Defines the rate at which the close float follows the main float when dragged. Only used when `CloseFloatConfig.closeBehavior` is `CloseBehavior.CLOSE_SNAPS_TO_MAIN_FLOAT`. | `Float` | `0.1f` |
## Contributing
Found a bug? Have a cool idea? Feel free to open an issue or submit a PR. We're all friends here!
Happy floating! 🎈