1234567
1234567

Reputation: 2485

@composable invocations can only happen from the context of an @composable function for Composable with LaunchedEffect and AndroidViewBinding

I have a composable function

@Composable
fun SomeComposeView(){
     
    AndroidViewBinding(SomefragactBinding::inflate) {
        val myFragment = fragmentContainerView.getFragment<SomeFragment>()
      myFragment.somefunction()  
   }

I want to call it only once so I am trying this in my Activity

binding.SomeComposeViewLayout.setContent {
             OwnTheme() {
                 LaunchedEffect(Unit){
                    SomeComposeView()
                }
            }
        }
    }

I have a function inside the fragment like myFragment.somefunction() It gets called twice, I want to ensure its called only once so I am trying to use LaunchedEffect how can we call the function only once

However I am getting the error @composable invocations can only happen from the context of an @composable function

Upvotes: 2

Views: 6611

Answers (2)

bh_earth0
bh_earth0

Reputation: 2834

if you get error @composable invocations can only happen from the context of an @composable function

put it as parameter. not in the body of fun.

try this;

LaunchedEffect(
   key="key1",
   SomeComposeView()
)

Upvotes: 0

Jolan DAUMAS
Jolan DAUMAS

Reputation: 1374

The LaunchedEffect error

You can't call a composable inside a LaunchedEffect. According to documentation, LaunchedEffect is meant to run suspend functions in the scope of a composable but not to render a composable.

To avoid your error you simply have to remove the LaunchedEffect an call your composable directly:

binding.SomeComposeViewLayout.setContent {
             OwnTheme() {
                
                    SomeComposeView()
               
            }
        }
    }

AndroidViewBinding interop

The AndroidViewBinding composable has 3 parameters :

  • The inflater
  • A modifier
  • A callback on update.

You placed the call of myFragment.somefunction()in the update block without any conditions so it will run at each recomposition.

Solution 1: Using a state

Create a boolean state that tells you if you need to call myFragment.somefunction()


@Composable
fun SomeComposeView(){
    
var needToCallSomeFunction by remember { mutableStateOf(true) }

AndroidViewBinding(SomefragactBinding::inflate) {
        val myFragment = fragmentContainerView.getFragment<SomeFragment>()
    if(needToCallSomeFunction) {
        myFragment.somefunction()
        needToCallSomeFunction = false
    }
   }

}

Solution 2: Call it in factory

You could create the viewBinding in the factory and before returning it, using it to get your fragment and call your function :

    AndroidViewBinding(
        factory = { inflater, parent, attachToParent ->
            SomefragactBinding.inflate(inflater, parent, attachToParent).also {
                it.fragmentContainerView.getFragment<SomeFragment>().somefunction()
            }
        }
    )

Because it is in the factory, it will just run once

Upvotes: 3

Related Questions