Closed Diy2210 closed 4 years ago
Because the createContentWidget
function must return an object of Widget
class but you return object of Job
class from launch
function.
It is incorrect to run asynchronous coroutines inside the synchronous widget creation function createContentWidget
. You send the request to the server that will send a response at an unspecified time. The screen will be rendered before the server sends a response. This is why widgets should observe data reactively something like this:
private val widgetData = MutableLiveData<String>("")
private fun launchAsyncRequest() {
coroutineScope.launch {
try {
client.getStatusServer(url, "/endpoint", token) { response ->
if (response.contains("success")) {
widgetData.value = response
} else {
widgetData.value = "Error"
}
}
} catch (exception: Exception) {
widgetData.value = "Error"
}
}
}
override fun createContentWidget(): Widget<WidgetSize.Const<SizeSpec.AsParent, SizeSpec.AsParent>> {
launchAsyncRequest()
return with(theme) {
container(size = WidgetSize.AsParent) {
text(
size = WidgetSize.WrapContent,
text = widgetData.map { it.desc() }
)
}
}
}
But it's bad way too. Do you use moko-mvvm library? A more correct way to run async code, launch all coroutines and send all requests to the server is to use ViewModel
class. You can find the example of combined usage of moko-widgets and moko-mvvm here. Example of screen and viewmodel for the screen.
Tnx!
I have one more question. I create Model from screen and create fun async. How call this fun in screen and get parse value. My Model: My Screen code:
init
block of the ViewModel
class.LiveData
objects, because widgets will get all data reactivly.class DetailsViewModel : VewModel() {
// ...
private val _hostName = MutableLiveData<String>("initString")
val hostName: LiveData<StringDesc> = _hostName.map { it.desc() }
init {
launchAsync()
}
fun launchAsync() {
viewModelScope.launch {
// ...
_hostName.value = resultString
}
}
}
And in the WidgetScreen:
override fun createContentWidget(): Widget<WidgetSize.Const<SizeSpec.AsParent, SizeSpec.AsParent>> {
val viewModel = getViewModel {
createViewModel()
}
return with(theme) {
container(size = WidgetSize.AsParent) {
text(
size = WidgetSize.WrapContent,
text = viewModel.hostname
)
}
}
}
How use LiveData objects, else I use Model. I try add LiveData and use it in text, but text field is empty. My model data class:
My Screen Model:
Screen code:
Because you should change object in value
property of the MutableLiveData
, not the properties values of the object. So, change value of LiveData
by creating new object oh the HostModel
class:
data class HostModel(
val hostName: String,
val os: Int,
val ip: String
)
val mutableLiveData = MutableLiveData<HostModel>(HostModel("Empty", 0, "0.0.0.0"))
fun launchAsyncRequest() {
// ...
mutableLiveData.value = HostModel(
hostName = newHostName,
os = newOs,
ip = newIp
)
// ...
}
Its work, thx again!
Last question. How implement MutableLiveData type Array(String)?
For array:
val stringArrayLiveData = MutableLiveData<Array<String>>(emptyArray())
stringArrayLiveData.value = arrayOf("1", "2", "3")
For list:
val stringListLiveData = MutableLiveData<List<String>>(emptyList())
stringListLiveData.value = listOf("1", "2", "3")
Cool Tnx!
Hi, I try parse in Screen using Coroutine, but createContentWidget() print error, I dont understand why, plz help. My code: