Search code examples
androidmutablecomposablemutablestateof

What is the syntax to make a callback function variable mutable in Jetpack Compose?


I'm trying to make the following variable, selectedView, mutable so that I can set the current Composable View dynamically.

var selectedView :@Composable () -> Unit =  { testView() }

I'm passing back the Composable function and the following code works, but I can't figure out how to make the variable selectedView updateable. This would work if selectedView variable was mutable.

@Composable
fun testView1() {
    Text("It works! 111")
}

...
    
val navBarItems = listOf(
    NavBarItem("Invite", Icons.Outlined.Send) { testView1() },
    NavBarItem("Messages", Icons.Outlined.Menu) { testView2() },
    NavBarItem("Groups", Icons.Outlined.AccountCircle) { testView3() }
)

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CurrentView() {
    
    var selectedView :@Composable () -> Unit =  { testView() }

    Scaffold(
        bottomBar = {
            CustomNavBar(navBarItems){
                selectedView  = it
            }
        }
    )
    {
        Box(modifier = Modifier
            .fillMaxSize()
            .padding(it), contentAlignment = Alignment.Center) {
            selectedView?.let { it1 -> it1() }
        }
    }
}

Any assistance would be greatly appreciated.


Solution

  • You can use Compose mutable state to hold it. Something like

    import androidx.compose.runtime.getValue
    import androidx.compose.runtime.setValue
    ...
    var selectedView by remember {
        mutableStateOf<@Composable () -> Unit>({ TestView() })
    }
    

    (Note - convention is that @Composable functions are named with UpperCamelCase)

    I see what you're trying to do here, but the problem is going to be dealing with configuration changes. Keeping track of that lambda inside CurrentView, it'll get lost and reinitialized on configuration change, such as a screen rotation.

    You'd be better served by keeping track of the current screen as a simple value (String, object, enum, etc) as a screen identifier inside a view model (with a saved state handler) or at least in a rememberSaveable.

    You could then define either a when on the screen identifier to select which @Composable to call, or put your lambdas inside a map keyed by the screen identifier.