I have tried to basic composable codelab exercise. In Android Studio BasicCodelabThemes shows as an error. Please help me to find the error
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApp {
Greeting("Android")
}
}
}
}
#Composable
fun MyApp(content:#Composable () -> Unit) {
BasicsCodelabTheme {
Surface(color = Color.Yellow) {
content()
}
}
}
#Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
Yeah from my research on the BasicCodelabTheme, it is a custom-built composable function, it isn't a predefined one, so you need to create it yourself in your Kotlin file as a composable function for your theme.BasicCodelabTheme function definition
Related
I'm having issues whit the onClick on Jetpack compose, it performs the click as soon as I run the app and after returning to this activity the button stops working. Any insights?
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val context = LocalContext.current
linkButton("Go to text views", goTo(context, TextViewActivity::class.java))
}
}
}
#Composable
fun linkButton(msg: String, link: Unit) {
Button(onClick = {
link
}) {
Text(msg)
}
}
#Preview
#Composable
fun PreviewMessageCard() {
val context = LocalContext.current
linkButton(
msg = "Sample",
link = goTo(context, TextViewActivity::class.java)
)
}
private fun goTo(context: Context, clazz: Class<*>) {
context.startActivity(Intent(context, clazz))
}
You are actually calling the method at the moment you are composing the linkButton, not passing it as a callback to be called on click. And on click, it is just returning a Unit which causes the unexpected behavior.
To fix that, you should change the parameter type in your composable function to () -> Unit, which represents a function type.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val context = LocalContext.current
linkButton("Go to text views") {
goTo(context, TextViewActivity::class.java)
}
}
}
}
#Composable
fun LinkButton(msg: String, link: () -> Unit) {
Button(onClick = {
link()
}) {
Text(msg)
}
}
I cannot collect any state from the same Jetpack Compose screen(JCScreen), after having it wrapped in a NavHost.
The original working solution:
this JcScreen is for user sign in
the activity ActAuth
#AndroidEntryPoint
class ActAuth : AppCompatActivity() {
private val viewModel: ViewModelAuth by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ThemeBasicHRLQ {
ScreenSignInWithInputValidationDebounce()
}
}
// to collect the state `stateSignIn`,
// and it works fine
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.stateAuth.collect { state ->
...
}
}
}
}
}
the digest of the JCScreen ScreenSignInWithInputValidationDebounce():
#OptIn(ExperimentalComposeUiApi::class, kInternalCoroutinesApi::class)
#Composable
fun ScreenSignInWithInputValidationDebounce(
viewModel: ViewModelAuth = hiltViewModel()
){
....
val stateSignIn by viewModel.stateAuth.collectAsState()
...
}
Everything works fine until I consolidating highly similar JCScreen for user sign up.
#AndroidEntryPoint
class ActAuth : AppCompatActivity() {
private val viewModel: ViewModelAuth by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ThemeBasicHRLQ {
// the new JCScreen
ScreenAuth()
}
}
// to collect the state `stateSignIn`
// but, with the new JCScreen, it does not work any more
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.stateAuth.collect { state ->
...
}
}
}
}
}
#Composable
fun ScreenAuth() {
val navController = rememberNavController()
NavHostAuth(navController = navController)
}
#Composable
fun NavHostAuth(
navController: NavHostController,
) {
NavHost(
navController = navController,
startDestination = NavRoutesAuth.SignIn.route
) {
// I am still in this same JCScreen,
// without using the `navController`
composable(NavRoutesAuth.SignIn.route) {
ScreenSignInWithInputValidationDebounce(navController)
}
composable(NavRoutesAuth.SignUp.route) {
ScreenSignUpWithInputValidationDebounce(navController)
}
}
}
enum class NavRoutesAuth(val route: String) {
SignIn("auth/signin"),
SignUp("auth/signup"),
}
It looks to me that once wrapped into a NavHost, the state management is different.
Here may be a similar problem, with which, however, I still cannot solve my problem above.
I have an BaseUi class for my custom views and my activities extends from it.
whe i using Root function , application crashed.
BaseUi.kt
#ExperimentalAnimationApi
open class BaseUi : AppCompatActivity() {
#Composable
fun RtlView(content: #Composable () -> Unit) {
CompositionLocalProvider(
LocalLayoutDirection provides LayoutDirection.Rtl,
content = content
)
}
#Composable
fun LtrView(content: #Composable () -> Unit) {
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
content()
}
}
#Composable
fun Root(
content: #Composable () -> Unit
) {
KasbTheme {
Box(
Modifier
.fillMaxSize()
.background(LightPageBackground)
.padding(Dimen.pagePadding)
){
content()
}
}
}
}
SplashActivity.kt
#ExperimentalAnimationApi
class SplashActivity : BaseActivity() {
val viewModel = SplashActivityViewModel()
#ExperimentalAnimationApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
InitUI()
}
}
#ExperimentalAnimationApi
#Preview(showBackground = true)
#Composable
fun InitUI() {
Root {
RtlView {
Box(Modifier.fillMaxSize()) {
Image(
painter = painterResource(id = R.drawable.logo),
contentDescription = "",
modifier = Modifier.size(200.dp).align(Alignment.Center)
)
}
}
}
}
}
Runtime error
com.kasb.android E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.kasb.android, PID: 22387
java.lang.ClassCastException: androidx.compose.runtime.internal.ComposableLambdaImpl cannot be cast to kotlin.jvm.functions.Function0
at com.kasb.android.ui.activity.SplashActivity.InitUI(SplashActivity.kt:76)
at com.kasb.android.ui.activity.SplashActivity$onCreate$1.invoke(SplashActivity.kt:30)
at com.kasb.android.ui.activity.SplashActivity$onCreate$1.invoke(SplashActivity.kt:29)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
at androidx.compose.ui.platform.ComposeView.Content(ComposeView.android.kt:384)
not a permanent fix, but clean project gets rid of the error for one compile. Don't know why it's happening though.
I had same error, and problem was caused that I was calling function form subtype of the class where it was defined
BaseActivity{
fun displayAlertDialog(content: #Composable() () -> Unit) {
bottomDialog.show(supportFragmentManager, "bottom-dialog")
bottomDialog.setContent(content)
}
#Composable
fun Screen(
activity: BaseActivity,
) {
activity.displayAlertDialog{}
}
// works fine
//if activity is subtype of BaseActivity it throws that error
I wanted to build a very simple demo. A button which you can click, and it counts the clicks.
Code looks like this:
class MainActivity : ComponentActivity() {
private var clicks = mutableStateOf(0)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Surface(color = MaterialTheme.colors.background) {
NewsStory(clicks.value) { onClick() }
}
}
}
private fun onClick() {
clicks.value++
}
}
#Composable
fun NewsStory(clicks: Int, onClick: () -> Unit) {
Column(modifier = Modifier.padding(8.dp)) {
Button(onClick = onClick) {
Text("Clicked: $clicks")
}
}
}
From my understanding this should be recomposed everytime the button is clicked, as clicks is changed.
But it does not work, any ideas what I'm doing wrong here?
I'm on androidx.activity:activity-compose:1.3.0-beta01, kotlin 1.5.10 and compose version 1.0.0-beta08
You need to use the "remember" keyword for the recomposition to happen each time, as explained here: https://foso.github.io/Jetpack-Compose-Playground/general/state/
In short, your composable would look like this:
#Composable
fun NewsStory (){
val clickState = remember { mutableStateOf(0) }
Column (modifier = Modifier.padding(8.dp)) {
Button(
onClick = { clickState.value++ }) {
}
Text("Clicked: $clickState.value.toString()")
}
}
There is a similar question Another similar question.
But it doesn't solve my problem.
I get this error "#Composable invocations can only happen from the context of a #composable function" after building my project, even though the preview is working fine.
This is my code:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApp {
SplashUI()
}
}
}
#Composable
fun SplashUI() {
Image(painterResource(R.drawable.logo_voodlee),"content description")
}
#Composable
fun MyApp(content: #Composable () -> Unit) {
MaterialTheme {
Surface(color = Color.Yellow) {
content()
}
}
}
#Preview("MyScreen preview")
#Composable
fun DefaultPreview() {
MyApp {
SplashUI()
}
}
Been stuck at this for hours.
Please help me fix this!!!
Found the solution. I had imported the wrong setContent, and had missed adding the dependency "androidx.activity:activity-compose:1.3.0-alpha05"
Added it, and then imported the right setContent, i.e androidx.activity.compose.setContent - this solved the issue.