Skip to content

Instantly share code, notes, and snippets.

View linean's full-sized avatar

Maciej Sady linean

View GitHub Profile
@linean
linean / StateMachineDebounceExample.kt
Created February 9, 2023 10:49
Simple example how events can be debounced on StateMachine side
// Keep in mind this is just simplified example
class Example {
private val scope = CoroutineScope(Job())
private val debouncingChannel = Channel<UserEvent>(
capacity = RENDEZVOUS,
onBufferOverflow = BufferOverflow.DROP_OLDEST,
)
LaunchedEffect(itemKeys) {
items.forEachIndexed { index, item ->
val newOffset = gridOffsets[index]
val itemOffset = itemsOffsets.getValue(itemKey(item))
launch { itemOffset.animateTo(newOffset, animationSpec) }
}
}
items.forEach { item ->
val offset = itemsOffsets.getValue(itemKey(item)).value
Box(
modifier = Modifier
.size(itemSize)
.offset(offset.x, offset.y)
) {
itemContent(item)
}
}
var itemsOffsets by remember { mutableStateOf(mapOf<KEY, ItemOffset>()) }
key(itemKeys) {
itemsOffsets = items.mapIndexed { index, item ->
val key = itemKey(item)
key to when {
itemsOffsets.containsKey(key) -> itemsOffsets.getValue(key)
else -> ItemOffset(gridOffsets[index])
}
}.toMap()
}
typealias ItemOffset = Animatable<DpOffset, AnimationVector2D>
fun ItemOffset(offset: DpOffset): ItemOffset = Animatable(offset, DpOffset.VectorConverter)
val gridOffsets = remember(columns, rows, itemSize) {
(0 until columns).map { column ->
(0 until rows).map { row ->
DpOffset(
x = itemSize.width * row,
y = itemSize.height * column,
)
}
}.flatten()
}
val itemSize = remember(columns, rows) {
val itemWidth = (maxWidth) / rows
val itemHeight = (maxHeight) / columns
DpSize(itemWidth, itemHeight)
}
@Composable
fun <ITEM, KEY> AnimatedVerticalGrid(
items: List<ITEM>,
itemKey: (ITEM) -> KEY,
columns: Int,
rows: Int
)
class ExampleActivity : Activity() {
private val viewModel = ViewModel()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.state.handleEvent(ExampleEvent)
}
}
class ViewModel {
val state = StateReducerFlow(
initialState = State(),
reduceState = ::reduceState
)
private fun reduceState(currentState: State, event: Event): State {
return when (event) {
is Increment -> currentState.copy(counter = currentState.counter + 1)