I am trying to build simple app using Jetpack Compose.
I followed this documentation, downloaded repository and created my own module.
Code is pretty simple:
import android.app.Activity
import android.os.Bundle
import androidx.compose.Composable
import androidx.ui.core.Text
import androidx.ui.core.setContent
import androidx.ui.material.surface.Card
import androidx.ui.graphics.Color
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApp()
}
}
#Composable
fun MyApp() {
Card(color = Color.Cyan) {
Text("test")
}
}
}
But I noticed that some composable widgets doesnt work and I have following error:
Exception while analyzing expression at (23,9) in
/path/Projects/androidx-master-dev/frameworks/support/ui/compose/src/main/java/app/myown/MainActivity.kt
Where (23,9) references to Card widget
By the way other widgets work, for example I dont have problems with
#Composable
fun MyApp() {
Padding(10.dp) {
Text("test")
}
}
It compiles and runs perfectly.
I got following problem with:
Card
Column
Row
Center
FlexColumn
and I guess many others widgets
I ran into this problem earlier.
There is an implicit need to have import androidx.compose.composer in every Kotlin source file that has #Composable functions. I say "implicit" because Android Studio thinks that it is unnecessary and has a tendency to remove that line (e.g., you ask it to optimize imports). Some #Composable functions can survive without this import, but others cannot.
As I understand it, this is one of those things that will get better as the libraries and tooling evolve, but at the moment, just keep an eye out for that import and add it if it is missing and you are getting weirder-than-normal results.
Related
Methods "map" and "filter" have been deprecated with the upgrade of Kotlin Coroutine Version. Code was working perfectly fine with earlier version of Kotlin Coroutine, due to some reason I need to upgrade coroutine version. But having much idea about it.
import kotlinx.coroutines.channels.BroadcastChannel
import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.channels.filter
import kotlinx.coroutines.channels.map
object EventBus {
#ObsoleteCoroutinesApi
val bus: BroadcastChannel<Any> = BroadcastChannel(1)
#ObsoleteCoroutinesApi
fun send(o: Any) {
CoroutineScope(Dispatchers.IO).launch {
bus.send(o)
}
}
#ObsoleteCoroutinesApi
inline fun <reified T> asChannel(): ReceiveChannel<T> {
return bus.openSubscription().filter { it is T }.map { it as T }
}
}````
Can we replace the above usage of Map and Filter? Any help would be greatly appreciated.
You should take annotation #ObsoleteCoroutinesApi serious, because
class BroadcastChannel had been deprecated altogether ...instead use SharedFlow or MutableSharedFlow; also see this example.
This blog post should support the understanding & migration: Shared flows, broadcast channels.
I followed multiple tutorials on how to implement this new nonsense.
I removed
apply plugin: `kotlin-android-extensions`
from gradle, added:
android {
...
buildFeatures {
viewBinding true
}
}
and then in activity (not MainActivity but another one because it's the first one that creates error on rebuild):
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
totorials are showing that the views have to be used this way now:
binding.whatEverView
but I still have unresolved reference on those views.
Now I'm wondering if the correct class gets automatically imported which is:
import de.blaa.blaaaa.databinding.ActivityMainBinding
None of the tutorials are showing what actual class supposed to be imported so is this the correct one? What am I missing?
Check that the id of "whateEverView" doesn't have a leading underscore or something.
In my case, I found that my field had an id of "_what_ever_view" in the layout xml. Changing it to "what_ever_view" fixed it.
Not sure if the problem is limited to leading underscores, or even if it's a specific version of the plug-in, but it does appear to be a bug. In my case, the issue field appears totally valid during code editing (code completion knows about the field), but the "unresolved" issue appears during build time.
I've been exploring Jetpack Compose lately and discovered this very weird scenario where my #Preview is not showing and Android Studio is literally showing blank empty. No warnings or errors are shown.
This happens when I add #PreviewParameter to my composable function parameter.
The stupid solution was… It turns out that I had my PreviewParameterProvider declared in private and turning it to public or simply removing the visibility modifier fixed it.
class MyProvider : PreviewParameterProvider<MyClass> {
...
}
This wasn't even documented.
I hope Google will make it clear in the documentation or at least give an error in Android Studio so developers won't encounter this frustrating and time-wasting scenario.
I had another issue causing the same behaviour.
blank screen
#Preview
#Composable
fun MyComposablePreview(
#PreviewParameter(MyComposableStateProvider::class) state: MyComposableState
) = MyComposable(state)
works
#Preview
#Composable
fun MyComposablePreview(
#PreviewParameter(MyComposableStateProvider::class) state: MyComposableState
) {
MyComposable(state)
}
I can also add that your implementation of PreviewParameterProvider should not be inner class and should not be inside other class. So
class MyFragment : Fragment() {
class MyProvider : PreviewParameterProvider<MyComposableStateProvider> {
...
}
#Preview
#Composable
fun MyComposablePreview(
#PreviewParameter(MyProvider::class) state: MyComposableState
) {
MyComposable(state)
}
}
won't work either. extract providers to separate files.
I want to use the state by delegate syntax on observeAsState, but it report a error show there is no getValue method in data class.
#Composable
fun ComposeScreen(
...
) {
val item: Item by viewModel.item.observeAsState(Item) // there is an error in `(Item)`, it seems not delegate directly using the model of data class.
}
// viewModel
val item = itemRepository.item // item is a LiveData
// model
data class Item(
...
)
UPDATE
I find the solution reference other one's demo project, but I still not understand why do this.
import androidx.compose.runtime.getValue
val item: Item? by viewModel.item.observeAsState()
You need to import it mannualy, like this:
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
or
import androidx.compose.runtime.*
I believe it's a bug in Android Studio where it does not suggest to import it automatically.
[EDIT]
You edited your question asking why we need that import, so I'll try to wrap it up:
When we want to delegate an assignment to a class using "by", we need to make a function called "getValue" with the "operator" modifier that returns the value in the correct type. It's the same logic for "setValue", we need to ask for a parameter of the right type and use "operator", allowing you to use "by" in a "var", besides "val".
What's happening there is that the function "getValue" and "setValue" are declared as extension functions, so it isn't enough to import "State", you also have to import the top level extension functions that are in a separated file.
Let me know if that explanation was enough, I took me a while to understand that, even thought that the documentation was out of date, by I was missing the import.
P.S. Here are the two functions in Compose's code, as you can see, they extends the "State" class.
inline operator fun <T> State<T>.getValue(thisObj: Any?, property: KProperty<*>): T = value
inline operator fun <T> MutableState<T>.setValue(
thisObj: Any?, property: KProperty<*>, value: T) {
this.value = value
}
I am trying to test ViewModel to make sure livedata gets updated correctly. However when using ArgumentMatchers.any() it fails with IllegalStateException saying:
ArgumentMatchers.any(mViewModel.CountSubscriber::class.java) must not
be null
#Test
fun emitValueIfCountIs7() {
doAnswer { invocation: InvocationOnMock ->
val subscriber: mViewModel.CountSubscriber = invocation.getArgument(0)
subscriber.onNext(7)
null
}.`when`(countUseCase).execute(
ArgumentMatchers.any(mViewModel.CountSubscriber::class.java),
ArgumentMatchers.any(Parameters::class.java)
)
// When
mViewModel.getCount()
// Verify
assert(mViewModel.countResponse.value != null)
}
I am using Kotlin and have the following dependencies:
testImplementation 'junit:junit:4.12'
testImplementation "org.mockito:mockito-inline:2.23.4"
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0"
Here are my imports:
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.nhaarman.mockitokotlin2.doAnswer
import io.reactivex.Observable
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.ArgumentMatchers.any
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import org.mockito.invocation.InvocationOnMock
Strange thing is that it used to work before, and I don't know what has happened that could affect this.
Getting matchers to work with Kotlin can be a problem.
If you have a method written in kotlin that does not take a nullable parameter, then we cannot match with it using Mockito.any().
This is because it can return void and this is not assignable to a non-nullable parameter.
If the method being matched is written in Java, then I think that it will work as all Java objects are implicitly nullable.
One possible solution would be to use a library like mockito-kotlin
But you can solve this issue easily with a few lines of code yourself.
If you need typed any(type: Class)
private fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
OR
You can use this matcher instead of Matchers.any() :
object MockitoHelper {
fun <T> anyObject(): T {
Mockito.any<T>()
return uninitialized()
}
#Suppress("UNCHECKED_CAST")
fun <T> uninitialized(): T = null as T
}
and use MockitoHelper.anyObject() instead of any() in your kotlin tests.
You can find more information in this post: Using Mockito with Kotlin
There is a discussion about possible solutions in this post :
Is it possible to use Mockito in Kotlin?
The correct solution is mentioned in the comment section of the question by #Ana Koridze. Yes, if you are using Koltin + mockitoKotlin. Make sure you are using the following import:
1 - Using the Mockito-kotlin:
import org.mockito.kotlin.any from Mockito-kotlin
instead of
import org.mockito.Mockito.any
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version"
2 - Or if you are using older mockito kotlin version original created by nhaarman before the intergation
import com.nhaarman.mockitokotlin2.any from nhaaram's Mockito-kotlin instead of import org.mockito.Mockito.any
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:$mockito_kotlin2_version"
BTW, if you are using Android Studio or IntelliJ IDEA. the any() from mockitokotlin library should be italic by default style/color scheme.
Notice the any() at the end of line. This is from mockitokotlin
And here is the any() from mockito
Thanks #Sattar for the recommended edit.
mockito-kotlin has added support for nullable args with anyOrNull()
`when`(myMock.doesThis(any(), anyOrNull())).thenReturn(someObject)
use Mockito-kotlin
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version"
this will use any that works for kotlin as this is a wrapper lib for Mockito but for kotlin
This is what worked for me,
either replaced all generic any()s , with a specific anyTYPE(), i.e anyInt(), anyList() etc from core lib org.mockito:mockito-core and it fixes the (nullability)issue, it seems the specific definitions with type can handle null. this option does not require you to import any extra lib
or
if you really need to use the generic type any() , add this official Mckito extension lib https://github.com/mockito/mockito-kotlin and make sure you use the any() from this lib (by making sure your imports has this in it import org.mockito.kotlin.any)
I suggest to start using MockK lib https://github.com/mockk/mockk instead of Mockito as it is a Mock library for Kotlin = MockK)
however, if you feel lazy to switch right now or maybe dealing with legacy tests (as in my case :), this should fix your issue too.
For me all solutions above were not enough - in addition to that I had to mark the called method as an 'open' method.
According to this:
https://github.com/mockito/mockito-kotlin/wiki/Parameter-specified-as-non-null-is-null
The method is final and Mockito couldn't mock it so I had to add 'open'.
I wrote a simple wrapper function around Mockito's any() and got rid of the warning.
private fun <T> any() : T {
return org.mockito.ArgumentMatchers.any()
}
I am still pretty green at Kotlin though, so I am not sure whether there may be some unwanted side effects. I put this answer out there in case it helps anyone or someone gives me feedback.