Skip to content

Instantly share code, notes, and snippets.

@Sintrastes
Last active March 22, 2024 02:09
Show Gist options
  • Save Sintrastes/c1c99fae5c8e505bbfffdfab60d8313c to your computer and use it in GitHub Desktop.
Save Sintrastes/c1c99fae5c8e505bbfffdfab60d8313c to your computer and use it in GitHub Desktop.
Messing around with compose runtime APIs
// Here's an example of how to return a state flow from the composable.
fun <A, B> buildComposeViewForm(
context: Context,
content: @Composable ViewScope.(A) -> B,
initialValue: A
): Pair<View, StateFlow<B>> {
val root = LinearLayout(context)
val applier = ViewApplier(root)
val clock = AndroidUiFrameClock(Choreographer.getInstance()) // BroadcastFrameClock()
val finalContext = Dispatchers.Main + clock
val parent = Recomposer(finalContext)
val composition = Composition(applier = applier, parent = parent)
val scope = object : ViewScope {
override val context: Context
get() = context
}
val coroutineScope = CoroutineScope(finalContext)
coroutineScope.launch {
parent.runRecomposeAndApplyChanges()
}
// Needed if not using the Android clock
// coroutineScope.launch(context = finalContext) {
// while (true) {
// clock.sendFrame(0L)
// delay(50)
// }
// }
Snapshot.registerGlobalWriteObserver {
coroutineScope.launch(finalContext) {
Snapshot.sendApplyNotifications()
}
}
var result: B? = null
var resultFlow: MutableStateFlow<B>? = null
composition.setContent {
result = scope.content(initialValue)
resultFlow?.value = result!!
}
resultFlow = MutableStateFlow(result!!)
return applier.root to resultFlow
}
package com.example.myapplication
import android.content.Context
import android.view.Choreographer
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.LinearLayout
import android.widget.TextView
import androidx.compose.runtime.*
import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.ui.platform.AndroidUiFrameClock
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class ViewApplier(root: ViewGroup) : AbstractApplier<View>(root) {
override fun insertBottomUp(index: Int, instance: View) {}
override fun insertTopDown(index: Int, instance: View) {
(current as? ViewGroup)
?.addView(instance, index)
}
override fun move(from: Int, to: Int, count: Int) {
val group = current as? ViewGroup
if (group != null) {
val moved = group.getChildAt(from)
group.removeViewAt(from)
group.addView(moved, to)
}
}
override fun onClear() {
(current as? ViewGroup)
?.removeAllViews()
}
override fun remove(index: Int, count: Int) {
for (i in 0 until count) {
(current as? ViewGroup)
?.removeViewAt(index + i)
}
}
}
interface ViewScope {
val context: Context
}
@Composable
fun ViewScope.Text(
text: String
) {
ComposeNode<View, ViewApplier>(
{
val view = TextView(context)
view.text = text
view
},
{
set(text) { newText ->
(this as TextView).text = newText
}
}
)
}
@Composable
fun ViewScope.Button(
onClick: () -> Unit
) {
ComposeNode<View, ViewApplier>(
{
val view = Button(context)
view.setOnClickListener {
onClick()
}
view
},
{
set(onClick) { newOnClick ->
(this as Button).setOnClickListener {
newOnClick()
}
}
}
)
}
@Composable
fun ViewScope.Column(content: @Composable () -> Unit) {
ComposeNode<View, ViewApplier>(
{ LinearLayout(context) },
{ },
content
)
}
fun buildComposeView(
context: Context,
content: @Composable ViewScope.() -> Unit
): View {
val root = LinearLayout(context)
val applier = ViewApplier(root)
val clock = AndroidUiFrameClock(Choreographer.getInstance()) // BroadcastFrameClock()
val finalContext = Dispatchers.Main + clock
val parent = Recomposer(finalContext)
val composition = Composition(applier = applier, parent = parent)
val scope = object : ViewScope {
override val context: Context
get() = context
}
val coroutineScope = CoroutineScope(finalContext)
coroutineScope.launch {
parent.runRecomposeAndApplyChanges()
}
// Needed if not using the Android clock
// coroutineScope.launch(context = finalContext) {
// while (true) {
// clock.sendFrame(0L)
// delay(50)
// }
// }
Snapshot.registerGlobalWriteObserver {
coroutineScope.launch(finalContext) {
Snapshot.sendApplyNotifications()
}
}
composition.setContent {
scope.content()
}
return applier.root
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment