Skip to content

Instantly share code, notes, and snippets.

@NitroG42
Last active August 23, 2022 09:52
Show Gist options
  • Save NitroG42/e0112edb828bbe83f12fa2c7f5832498 to your computer and use it in GitHub Desktop.
Save NitroG42/e0112edb828bbe83f12fa2c7f5832498 to your computer and use it in GitHub Desktop.
Useful Compose links/tips

Do a LazyList with a custom background (like a big rounded square)

https://gist.github.com/micHar/3926308e9467bac806f15f615b8a9c72

Summary

Each items has a different background (mainly first, last, remaining items share the same). It's the same than with recyclerview but here in compose the use of modifiers makes it really simple.

Andrey Kulikov samples

Contains simple solutions for things like snapping, infinite list, playing with input gesture.

https://gist.github.com/andkulikov/c5cd7cd2b5b5bc2e74a9dc7255d2ebdf

A tutorial showcase -> https://github.com/andkulikov/compose-photoapp/commit/0ce26695e7238185fb202f57ef323b50d4e5b96d

Nested Scrolling Sample

Albert Chang :

@Composable
fun Screen() {
    var collapsibleHeight by remember { mutableStateOf(0f) }
    var offset by remember { mutableStateOf(0f) }
    val connection = remember {
        object : NestedScrollConnection {
            private fun consume(available: Offset): Offset {
                val newOffset = (offset + available.y).coerceIn(-collapsibleHeight, 0f)
                return Offset(0f, newOffset - offset).also { offset = newOffset }
            }
​
            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset =
                if (available.y >= 0) Offset.Zero else consume(available)
​
            override fun onPostScroll(
                consumed: Offset,
                available: Offset,
                source: NestedScrollSource
            ): Offset = consume(available)
        }
    }
​
    CustomColumn(
        modifier = Modifier.nestedScroll(connection),
        offset = offset.roundToInt(),
        onCollapsibleHeightChanged = { collapsibleHeight = it.toFloat() }
    ) {
        Card(
            modifier = Modifier
                .fillMaxWidth()
                .height(400.dp)
                .padding(16.dp)
        ) {}
        Surface(
            shape = RoundedCornerShape(16.dp),
            elevation = 4.dp
        ) {
            LazyColumn(modifier = Modifier.fillMaxSize()) {
                items(100) { i ->
                    @OptIn(ExperimentalMaterialApi::class)
                    ListItem {
                        Text(text = i.toString())
                    }
                }
            }
        }
    }
}
​
@Composable
private fun CustomColumn(
    modifier: Modifier = Modifier,
    offset: Int = 0,
    onCollapsibleHeightChanged: (Int) -> Unit = {},
    content: @Composable () -> Unit
) {
    Layout(content, modifier) { measurables, constraints ->
        var collapsibleHeight = 0
        val placeables = measurables.mapIndexed { i, measurable ->
            val childConstraints = Constraints(
                maxWidth = constraints.maxWidth,
                maxHeight = if (i == measurables.lastIndex) {
                    constraints.maxHeight
                } else {
                    constraints.maxHeight - collapsibleHeight
                }
            )
            measurable.measure(childConstraints).also {
                if (i < measurables.lastIndex) collapsibleHeight += it.height
            }
        }
        onCollapsibleHeightChanged(collapsibleHeight)
        collapsibleHeight = 0
        layout(constraints.maxWidth, constraints.maxHeight) {
            placeables.forEachIndexed { i, placeable ->
                if (i == placeables.lastIndex) {
                    placeable.place(0, collapsibleHeight + offset)
                } else {
                    placeable.place(0, collapsibleHeight)
                    collapsibleHeight += placeable.height
                }
            }
        }
    }
}

Keep Screen On

https://kotlinlang.slack.com/archives/CJLTWPH7S/p1646998371832069

It uses the view mecanism + require an id declared in res but it's the cleanest way (+ handle ref counting)

private val View.keepScreenOnState: KeepScreenOnState
    get() = getTag(R.id.keep_screen_on_state) as? KeepScreenOnState
        ?: KeepScreenOnState(this).also { setTag(R.id.keep_screen_on_state, it) }

private class KeepScreenOnState(private val view: View) {
    private var refCount = 0
        set(value) {
            val newValue = value.coerceAtLeast(0)
            field = newValue
            view.keepScreenOn = newValue > 0
        }

    fun request() {
        refCount++
    }

    fun release() {
        refCount--
    }
}

@Composable
fun KeepScreenOnRequest() {
    val view = LocalView.current
    DisposableEffect(view) {
        val ksoState = view.keepScreenOnState
        ksoState.request()
        onDispose {
            ksoState.release()
        }
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment