Hi! I need to create composable wrapper function, which wraps another composable function and insid...
m
Hi! I need to create composable wrapper function, which wraps another composable function and inside getting all widgets that are rendered on screen (parent with childs) and that data can be refreshed only every recomposition, so I can keep newest data. I tried everything but I dont know how to achieve it. I need to get all widgets inside SampleComposasble (2 TextView and 1 Button) -> their position, id etc. How to traverse widgets in compose? To sum up: how to get composable widgets tree/hierarchy from composable function?
๐Ÿงต 2
s
m
Here is the snipped where I was trying to get all widgets that Im displaying in composable function, but Im not getting widgets at all:
Copy code
package com.example.jetpackcomposetraversewidgets

import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.jetpackcomposetraversewidgets.ui.theme.JetpackComposeTraverseWidgetsTheme

@Composable
fun <T> ComposableWrapper(
    composableFunction: @Composable () -> T
) {
    // State to hold the latest root view
    var rootView by remember { mutableStateOf<android.view.View?>(null) }

    Column {
        // Get the current root view
        val currentView = LocalView.current

        // LaunchedEffect to update the root view on recomposition
        LaunchedEffect(currentView) {
            rootView = currentView
        }

        // Call the composable function
        composableFunction()
    }

    // Display the root view information
    rootView?.let { view ->
        val childViews = getAllChildViews(view)
        Column(modifier = Modifier.padding(20.dp)) {
            Text("Root view hash code: ${view.hashCode()}, ${view.id}")
            childViews.forEach { child ->
                val viewInfo = "ID: ${child.id}, Type: ${child::class.java.simpleName}, x: ${child.x}, y: ${child.y}"
                Text(viewInfo, modifier = Modifier.padding(vertical = 4.dp))
            }
        }
    }
}

fun getAllChildViews(view: View): List<View> {
    val result = mutableListOf<View>()
    if (view is ViewGroup) {
        for (i in 0 until view.childCount) {
            val child = view.getChildAt(i)
            result.add(child)
            result.addAll(getAllChildViews(child))
        }
    } else {
        result.add(view)
    }
    return result
}

@Composable
fun SampleComposable() {
    Column {
        Text("Hello World", modifier = Modifier.padding(4.dp))
        Text("Test", modifier = Modifier.padding(4.dp))
//        Button(onClick = { /* Do something */ }) {
//            Text("Click me")
//        }
    }
}

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            JetpackComposeTraverseWidgetsTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    ComposableWrapper {
                        SampleComposable()
                    }
                }
            }
        }
    }
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    JetpackComposeTraverseWidgetsTheme {
        ComposableWrapper {
            SampleComposable()
        }
    }
}
s
that data can be refreshed only every recomposition, sol can keep newest
What you're describing is just what compose does by default. Is this about interop with the view system? What are you trying to achieve?
m
I need to get all of widgets that are rendered and visible on the screen so I can find specific widget and do something with it.
s
Why not do the thing on the widget itself, without doing any querying and such? Compose is a declarative UI toolkit. You write down how things are, not so much having an instance to a specific composable and sending things to it. Perhaps you want to read this https://developer.android.com/develop/ui/compose/mental-model to get more familiar with the idea in general. To me it looks like you're trying to do something which is not that useful in compose. I may be misunderstanding your intentions, but that's what I see so far in this code snippet.
m
I know what are the idea of jetpack compose ๐Ÿ™‚ But I need to dynamically add some behaviour to the components in runtime, so user select any widget on the screen and attach event to it. Is that why I have specific requirements about it to search widget tree somehow.
So image for example that I have mobile app which allow user to select any component on ui -> save events which are attached to specific user, so another user when launch app can see for example Snackbar when he touch button, or song start playing when he touch another component. So i was thinking that the first idea is to give tree widgets which user can select, and then find those components attached to events.