/
FrameController.kt
76 lines (67 loc) · 3.08 KB
/
FrameController.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package com.zachklipp.compose.backstack
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.ui.Modifier
import com.zachklipp.compose.backstack.FrameController.BackstackFrame
/**
* A stable object that processes changes to a [Backstack]'s list of screen keys, determining which
* screens should be actively composed at any given time, and tweaking their appearance by applying
* [Modifier]s.
*
* The [Backstack] composable will notify its controller whenever the backstack changes by calling
* [updateBackstack], but the controller is in full control of when those changes actually get
* reflected in the composition. For example, a controller may choose to keep some screens around
* for a while, even after they're removed from the backstack, in order to animate their removal.
*/
@Stable
interface FrameController<T : Any> {
/**
* The frames that are currently being active. All active frames will be composed. When a frame
* that is in the backstack stops appearing in this list, its state will be saved.
*
* Should be backed by either a [MutableState] or a [SnapshotStateList]. This property
* will not be read until after [updateBackstack] is called at least once.
*/
val activeFrames: List<BackstackFrame<T>>
/**
* Notifies the controller that a new backstack was passed in. This method must initialize
* [activeFrames] first time it's called, and subsequently should probably result in
* [activeFrames] being updated to show new keys or hide old ones, although the controller may
* choose to do that later (e.g. if one of the active frames is currently being animated).
*
* This method will be called _directly from the composition_ – it must not perform side effects
* or update any state that is not backed by snapshot state objects (such as [MutableState]s,
* lists created by [mutableStateListOf], etc.).
*
* @param keys The latest backstack passed to [Backstack]. Will always contain at least one
* element.
*/
fun updateBackstack(keys: List<T>)
/**
* A frame controlled by a [FrameController], to be shown by [Backstack].
*/
@Immutable
data class BackstackFrame<out T : Any>(
val key: T,
val modifier: Modifier = Modifier
)
}
/**
* Returns a [FrameController] that always just shows the top frame without any special effects.
*/
@Suppress("UNCHECKED_CAST")
fun <T : Any> NoopFrameController(): FrameController<T> = NoopFrameController as FrameController<T>
private object NoopFrameController : FrameController<Any> {
private var topFrame by mutableStateOf<BackstackFrame<Any>?>(null)
override val activeFrames: List<BackstackFrame<Any>>
get() = topFrame?.let { listOf(it) } ?: emptyList()
override fun updateBackstack(keys: List<Any>) {
topFrame = BackstackFrame(keys.last())
}
}