Jetpack Compose interface is never updated with new api data - api

I'm making my first app with Kotlin and Jetpack Compose, I’m still studying. The app, It is a meteorological application that takes the data from an API. The MainScreen starts with the data of the city of Madrid by default, and has a searchBar where you can search for any other city. My problem is that when a new city is introduced in the searchbar, this name travels to the backend and makes the calls to the API, the API responds with the new data but these never come back to the interface. The MainScreen is never updated with new data, but I can see the data on my console.
Any help in welcome, thanks in advance
MainScreen
#RequiresApi(Build.VERSION_CODES.O)
#SuppressLint("UnusedMaterialScaffoldPaddingParameter", "SimpleDateFormat")
#Composable
fun MainScreen(
MainViewModel: MainViewModel
) {
val acceso = Accesso_API()
acceso.coger_tiempo_nombre("Madrid")
while (!acceso.datos_adquiridos) {}
Log.println(Log.ASSERT, "Inicio MainScreen", acceso.resultado.toString())
Surface(
color = MaterialTheme.colors.background,
modifier = Modifier
.fillMaxHeight()
.fillMaxSize()
) {
val searchWidgetState by MainViewModel.searchWidgetState
val searchTextState by MainViewModel.searchTextState
Scaffold(
modifier = Modifier
.fillMaxSize(),
topBar = {
MainAppBar(
searchWidgetState = searchWidgetState,
searchTextState = searchTextState,
onTextChange = {
MainViewModel.updateSearchTextState(newValue = it)
},
onCloseClicked = {
MainViewModel.updateSearchWidgetState(newValue = SearchWidgetState.CLOSED)
},
onSearchClicked = {
Log.d("Searched Text", it)
MainViewModel.searchTextState.value
Log.d("searchTextState", searchTextState)
acceso.coger_tiempo_nombre(searchTextState)
},
onSearchTriggered = {
MainViewModel.updateSearchWidgetState(newValue = SearchWidgetState.OPENED)
}
)
}
) {
Column(
modifier = Modifier
.background(Color.White)
.fillMaxWidth()
.padding(20.dp)
.verticalScroll(rememberScrollState())
) {
//TARJETA PRICIPAL
Card(
modifier = Modifier
.fillMaxWidth(),
backgroundColor = GreyCard,
elevation = 0.dp,
shape = RoundedCornerShape(10.dp)
) {
WeatherDisplayDailyCard(acceso.resultado)
}
//DETALLES
Row(
modifier = Modifier
.padding(
top = 5.dp,
bottom = 0.dp
)
.align(Alignment.CenterHorizontally),
horizontalArrangement = Arrangement.SpaceBetween,
) {
DetailsItem(
details = WeatherDetails(
units = "Pressure",
data = "${acceso.resultado.presion.toString()} mb",
R.drawable.pressure,
color = GreyCard
)
)
DetailsItem(
details = WeatherDetails(
units = "Wind",
data = "${acceso.resultado.viento.toString()} km/h",
R.drawable.wind2,
color = GreyCard
)
)
}
Row(
modifier = Modifier
.padding(
top = 0.dp,
bottom = 0.dp
)
.align(Alignment.CenterHorizontally),
horizontalArrangement = Arrangement.SpaceBetween,
) {
DetailsItem(
details = WeatherDetails(
units = "UV Index",
data = acceso.resultado.uv_text.toString(),
R.drawable.rays,
color = GreyCard
)
)
DetailsItem(
details = WeatherDetails(
units = "Humidity",
data = "${acceso.resultado.humedad_relativa.toString()} %",
R.drawable.water,
color = GreyCard
)
)
}
Spacer(modifier = Modifier.height(16.dp))
//12 HORAS
var hour_1 = acceso.resultado.hora_1;
var hour_2 = acceso.resultado.hora_2;
var hour_3 = acceso.resultado.hora_3;
var hour_4 = acceso.resultado.hora_4;
var hour_5 = acceso.resultado.hora_5;
var hour_6 = acceso.resultado.hora_6;
var hour_7 = acceso.resultado.hora_7;
var hour_8 = acceso.resultado.hora_8;
var hour_9 = acceso.resultado.hora_9;
var hour_10 = acceso.resultado.hora_10;
var hour_11 = acceso.resultado.hora_11;
var hour_12 = acceso.resultado.hora_12;
val formatterHour = DateTimeFormatter.ISO_DATE_TIME
val time1: LocalTime? = LocalTime.parse(hour_1, formatterHour)
val time2: LocalTime? = LocalTime.parse(hour_2, formatterHour)
val time3: LocalTime? = LocalTime.parse(hour_3, formatterHour)
val time4: LocalTime? = LocalTime.parse(hour_4, formatterHour)
val time5: LocalTime? = LocalTime.parse(hour_5, formatterHour)
val time6: LocalTime? = LocalTime.parse(hour_6, formatterHour)
val time7: LocalTime? = LocalTime.parse(hour_7, formatterHour)
val time8: LocalTime? = LocalTime.parse(hour_8, formatterHour)
val time9: LocalTime? = LocalTime.parse(hour_9, formatterHour)
val time10: LocalTime? = LocalTime.parse(hour_10, formatterHour)
val time11: LocalTime? = LocalTime.parse(hour_11, formatterHour)
val time12: LocalTime? = LocalTime.parse(hour_12, formatterHour)
HourlyForecastSheet(
hourlyForecast = listOf(
HourlyDetails(
hour = time1.toString(),
icon = acceso.resultado.icono_hora_1,
temperature = acceso.resultado.temperatura_hora_1
),
HourlyDetails(
hour = time2.toString(),
icon = acceso.resultado.icono_hora_2,
temperature = acceso.resultado.temperatura_hora_2
),
HourlyDetails(
hour = time3.toString(),
icon = acceso.resultado.icono_hora_3,
temperature = acceso.resultado.temperatura_hora_3
),
HourlyDetails(
hour = time4.toString(),
icon = acceso.resultado.icono_hora_4,
temperature = acceso.resultado.temperatura_hora_4
),
HourlyDetails(
hour = time5.toString(),
icon = acceso.resultado.icono_hora_5,
temperature = acceso.resultado.temperatura_hora_5
),
HourlyDetails(
hour = time6.toString(),
icon = acceso.resultado.icono_hora_6,
temperature = acceso.resultado.temperatura_hora_6
),
HourlyDetails(
hour = time7.toString(),
icon = acceso.resultado.icono_hora_7,
temperature = acceso.resultado.temperatura_hora_7
),
HourlyDetails(
hour = time8.toString(),
icon = acceso.resultado.icono_hora_8,
temperature = acceso.resultado.temperatura_hora_8
),
HourlyDetails(
hour = time9.toString(),
icon = acceso.resultado.icono_hora_9,
temperature = acceso.resultado.temperatura_hora_9
),
HourlyDetails(
hour = time10.toString(),
icon = acceso.resultado.icono_hora_10,
temperature = acceso.resultado.temperatura_hora_10
),
HourlyDetails(
hour = time11.toString(),
icon = acceso.resultado.icono_hora_11,
temperature = acceso.resultado.temperatura_hora_11
),
HourlyDetails(
hour = time12.toString(),
icon = acceso.resultado.icono_hora_12,
temperature = acceso.resultado.temperatura_hora_12
),
)
)
Spacer(modifier = Modifier.height(8.dp))
//5 DIAS
Column()
{
val date_1 = acceso.resultado.fecha_dia_1
val date_2 = acceso.resultado.fecha_dia_2
val date_3 = acceso.resultado.fecha_dia_3
val date_4 = acceso.resultado.fecha_dia_4
val date_5 = acceso.resultado.fecha_dia_5
val formatterDate = DateTimeFormatter.ISO_DATE_TIME
val day_1: OffsetDateTime = OffsetDateTime.parse(date_1, formatterDate)
val day_2: OffsetDateTime = OffsetDateTime.parse(date_2, formatterDate)
val day_3: OffsetDateTime = OffsetDateTime.parse(date_3, formatterDate)
val day_4: OffsetDateTime = OffsetDateTime.parse(date_4, formatterDate)
val day_5: OffsetDateTime = OffsetDateTime.parse(date_5, formatterDate)
val date1: DayOfWeek? = LocalDate.from(day_1).dayOfWeek
val date2: DayOfWeek? = LocalDate.from(day_2).dayOfWeek
val date3: DayOfWeek? = LocalDate.from(day_3).dayOfWeek
val date4: DayOfWeek? = LocalDate.from(day_4).dayOfWeek
val date5: DayOfWeek? = LocalDate.from(day_5).dayOfWeek
Next5daysForecastItem(
Next5daysForecast(
date_5days = date1.toString(),
icon_5days = acceso.resultado.icono_dia_1,
temperature_min_5days = acceso.resultado.Temp_min_dia_1,
temperature_max_5days = acceso.resultado.Temp_max_dia_1,
icon_frase_5days = acceso.resultado.frase_dia_1,
)
)
Next5daysForecastItem(
Next5daysForecast(
date_5days = date2.toString(),
icon_5days = acceso.resultado.icono_dia_2,
temperature_min_5days = acceso.resultado.Temp_min_dia_2,
temperature_max_5days = acceso.resultado.Temp_max_dia_2,
icon_frase_5days = acceso.resultado.frase_dia_2,
)
)
Next5daysForecastItem(
Next5daysForecast(
date_5days = date3.toString(),
icon_5days = acceso.resultado.icono_dia_3,
temperature_min_5days = acceso.resultado.Temp_min_dia_3,
temperature_max_5days = acceso.resultado.Temp_max_dia_3,
icon_frase_5days = acceso.resultado.frase_dia_3,
)
)
Next5daysForecastItem(
Next5daysForecast(
date_5days = date4.toString(),
icon_5days = acceso.resultado.icono_dia_4,
temperature_min_5days = acceso.resultado.Temp_min_dia_4,
temperature_max_5days = acceso.resultado.Temp_max_dia_4,
icon_frase_5days = acceso.resultado.frase_dia_4,
)
)
Next5daysForecastItem(
Next5daysForecast(
date_5days = date5.toString(),
icon_5days = acceso.resultado.icono_dia_5,
temperature_min_5days = acceso.resultado.Temp_min_dia_5,
temperature_max_5days = acceso.resultado.Temp_max_dia_5,
icon_frase_5days = acceso.resultado.frase_dia_5,
)
)
Log.println(Log.ASSERT, "Final MainScreen", acceso.resultado.toString())
}
}
}
}
}
MainViewModel
#RequiresApi(Build.VERSION_CODES.O)
class MainViewModel : ViewModel() {
private val _searchWidgetState: MutableState<SearchWidgetState> =
mutableStateOf(value = SearchWidgetState.CLOSED)
val searchWidgetState: State<SearchWidgetState> = _searchWidgetState
private val _searchTextState: MutableState<String> =
mutableStateOf(value = "")
val searchTextState: State<String> = _searchTextState
fun updateSearchWidgetState(newValue: SearchWidgetState) {
_searchWidgetState.value = newValue
}
fun updateSearchTextState(newValue: String) {
_searchTextState.value = newValue
}
}
API_Access
class Accesso_API {
val api_key ="87EYQdAb8jlViEELJJ6KsgJGfkcKG6qu"
#RequiresApi(Build.VERSION_CODES.O)
var resultado: Datos_Tiempo = Datos_Tiempo()
var datos_adquiridos: Boolean = false
#RequiresApi(Build.VERSION_CODES.O)
fun coger_tiempo_posicion_gps(longitud: Float, latitud: Float) {
coger_datos("", longitud,latitud,"")
}
#RequiresApi(Build.VERSION_CODES.O)
fun coger_tiempo_nombre(
nombre: String,
) {
coger_datos(nombre, 0.0f,0.0f,"")
}
#RequiresApi(Build.VERSION_CODES.O)
fun coger_tiempo_key(key: String) {
coger_datos("", 0.0f,0.0f,key)
}
#RequiresApi(Build.VERSION_CODES.O)
private fun coger_datos(nombre: String, longitud: Float, latitud: Float, p_key: String) {
datos_adquiridos = false
val quotesApi = RetrofitHelper.getInstance().create(QuotesApi_Tiempo::class.java)
// launching a new coroutine
GlobalScope.launch {
var key = "308526"
var ciudad_texto = "Sevilla"
Log.println(Log.ASSERT, "", "Ciudad entregada:" + nombre)
if (nombre != "") {
Log.println(Log.ASSERT, "", "Ciudad entregada por nombre:" + nombre)
// Coger la respuesta del rest API de la consulta de localidad por nombre
var respuesta = quotesApi.get_localidad_nombre(nombre, apikey = api_key)
Log.println(Log.ASSERT, "respuesta", respuesta.toString())
// Convertir la respuesta al objeto: List<Localidad>
var ciudades = respuesta.body()
key = ciudades?.get(0)?.Key.toString()
ciudad_texto = ciudades?.get(0)?.EnglishName.toString()
Log.println(Log.ASSERT, "", key)
Log.println(Log.ASSERT, "", ciudad_texto)
}
else {
if (p_key == "") {
// Coger la respuesta del rest API de la consulta de localidad por geoposición (longitud y latitud)
var respuesta = quotesApi.get_localidad_geoposition(
q = longitud.toString() + "," + latitud.toString(),
apikey = api_key
)
// Convertir la respuesta al objeto: Localidad
var ciudad = respuesta.body()
key = ciudad?.Key.toString()
ciudad_texto = ciudad?.localizedName.toString()
} else {
key = p_key
}
}
// Coger la respuesta del rest API de la consulta de las condiciones actuales
val respuesta_cond_act = quotesApi.get_condiciones_actuales(localidad = key, apikey = api_key, details = true, metric = true)
// Convertir la respuesta al objeto: List<Condiciones>
val cond_act = respuesta_cond_act.body()
// Coger la respuesta del rest API de la consulta de la previción de 1 dia
val respuesta_prev_1day = quotesApi.get_prevision_1_dia(localidad = key, apikey = api_key, details = true, metric = true)
// Convertir la respuesta al objeto: Previccion
val prev_1day = respuesta_prev_1day.body()
// Coger la respuesta del rest API de la consulta de la previcción de 5 dias
val respuesta_prev_5d = quotesApi.get_prevision_5_dias(localidad = key, apikey = api_key, details = true, metric = true)
// Convertir la respuesta al objeto: Previccion_5dias
val prev_5d = respuesta_prev_5d.body()
// Coger la respuesta del rest API de la consulta de la previcción de 12 horas
val respuesta_prev_12h = quotesApi.get_prevision_12_horas(localidad = key, apikey = api_key, details = true, metric = true)
// Convertir la respuesta al objeto: List<Previccion_12Horas>
val prev_12h = respuesta_prev_12h.body()
resultado.ciudad = ciudad_texto
// CARTA PRICIPAL
if (cond_act?.get(0) != null) {
resultado.temperatura_actual = cond_act?.get(0)?.Temperature?.Metric?.Value
resultado.unidades_temperatura = cond_act?.get(0)?.Temperature?.Metric?.UnitType.toString()
resultado.icono = cond_act?.get(0)?.WeatherIcon
resultado.icono_frase = cond_act?.get(0)?.WeatherText
}
if (prev_1day != null) {
resultado.temperatura_maxima = prev_1day?.DailyForecasts?.get(0)?.Temperature?.Maximum?.Value
resultado.temperatura_minima = prev_1day?.DailyForecasts?.get(0)?.Temperature?.Minimum?.Value
}
// DETALLES
if (cond_act?.get(0) != null) {
resultado.presion = cond_act?.get(0)?.Pressure?.Metric?.Value
resultado.uv = cond_act?.get(0)?.UVIndex
resultado.uv_text = cond_act?.get(0)?.UVIndexText
resultado.humedad_relativa = cond_act?.get(0)?.RelativeHumidity
resultado.viento = cond_act?.get(0)?.Wind?.Speed?.Metric?.Value
resultado.direccion_viento = cond_act?.get(0)?.Wind?.Direction?.Localized
}
// 12 HORAS
if (prev_12h?.get(0) != null) {
resultado.hora_1 = prev_12h?.get(0)?.DateTime?.toString().toString()
resultado.temperatura_hora_1 = prev_12h?.get(0)?.Temperature?.Value!!
resultado.icono_hora_1 = prev_12h.get(0).WeatherIcon!!
resultado.hora_2 = prev_12h.get(1).DateTime.toString()
resultado.temperatura_hora_2 = prev_12h.get(1).Temperature?.Value!!
resultado.icono_hora_2 = prev_12h.get(1).WeatherIcon!!
resultado.hora_3 = prev_12h.get(2).DateTime.toString()
resultado.temperatura_hora_3 = prev_12h.get(2).Temperature?.Value!!
resultado.icono_hora_3 = prev_12h.get(2).WeatherIcon!!
resultado.hora_4 = prev_12h?.get(3)?.DateTime.toString()
resultado.temperatura_hora_4 = prev_12h.get(3).Temperature?.Value!!
resultado.icono_hora_4 = prev_12h.get(3).WeatherIcon!!
resultado.hora_5 = prev_12h.get(4).DateTime.toString()
resultado.temperatura_hora_5 = prev_12h?.get(4)?.Temperature?.Value!!
resultado.icono_hora_5 = prev_12h?.get(4)?.WeatherIcon!!
resultado.hora_6 = prev_12h?.get(5)?.DateTime.toString()
resultado.temperatura_hora_6 = prev_12h?.get(5)?.Temperature?.Value!!
resultado.icono_hora_6 = prev_12h?.get(5)?.WeatherIcon!!
resultado.hora_7 = prev_12h?.get(6)?.DateTime.toString()
resultado.temperatura_hora_7 = prev_12h?.get(6)?.Temperature?.Value!!
resultado.icono_hora_7 = prev_12h?.get(6)?.WeatherIcon!!
resultado.hora_8 = prev_12h?.get(7)?.DateTime.toString()
resultado.temperatura_hora_8 = prev_12h?.get(7)?.Temperature?.Value!!
resultado.icono_hora_8 = prev_12h?.get(7)?.WeatherIcon!!
resultado.hora_9 = prev_12h?.get(8)?.DateTime.toString()
resultado.temperatura_hora_9 = prev_12h?.get(8)?.Temperature?.Value!!
resultado.icono_hora_9 = prev_12h?.get(8)?.WeatherIcon!!
resultado.hora_10 = prev_12h?.get(9)?.DateTime.toString()
resultado.temperatura_hora_10 = prev_12h?.get(9)?.Temperature?.Value!!
resultado.icono_hora_10 = prev_12h?.get(9)?.WeatherIcon!!
resultado.hora_11 = prev_12h?.get(10)?.DateTime.toString()
resultado.temperatura_hora_11 = prev_12h?.get(10)?.Temperature?.Value!!
resultado.icono_hora_11 = prev_12h?.get(10)?.WeatherIcon!!
resultado.hora_12 = prev_12h?.get(11)?.DateTime.toString()
resultado.temperatura_hora_12 = prev_12h?.get(11)?.Temperature?.Value!!
resultado.icono_hora_12 = prev_12h?.get(11)?.WeatherIcon!!
}
// 5 dias
if (prev_5d != null) {
resultado.fecha_dia_1 = prev_5d?.DailyForecasts?.get(0)?.Date.toString()
resultado.frase_dia_1 = prev_5d?.DailyForecasts?.get(0)?.Day?.ShortPhrase.toString()
resultado.icono_dia_1 = prev_5d?.DailyForecasts?.get(0)?.Day?.Icon!!
resultado.Temp_max_dia_1 = prev_5d?.DailyForecasts?.get(0)?.Temperature?.Maximum?.Value!!
resultado.Temp_min_dia_1 = prev_5d?.DailyForecasts?.get(0)?.Temperature?.Minimum?.Value!!
resultado.fecha_dia_2 = prev_5d?.DailyForecasts?.get(1)?.Date.toString()
resultado.frase_dia_2 = prev_5d?.DailyForecasts?.get(1)?.Day?.ShortPhrase.toString()
resultado.icono_dia_2 = prev_5d?.DailyForecasts?.get(1)?.Day?.Icon!!
resultado.Temp_max_dia_2 = prev_5d?.DailyForecasts?.get(1)?.Temperature?.Maximum?.Value!!
resultado.Temp_min_dia_2 = prev_5d?.DailyForecasts?.get(1)?.Temperature?.Minimum?.Value!!
resultado.fecha_dia_3 = prev_5d?.DailyForecasts?.get(2)?.Date.toString()
resultado.frase_dia_3 = prev_5d?.DailyForecasts?.get(2)?.Day?.ShortPhrase.toString()
resultado.icono_dia_3 = prev_5d?.DailyForecasts?.get(2)?.Day?.Icon!!
resultado.Temp_max_dia_3 =prev_5d?.DailyForecasts?.get(2)?.Temperature?.Maximum?.Value!!
resultado.Temp_min_dia_3 = prev_5d?.DailyForecasts?.get(2)?.Temperature?.Minimum?.Value!!
resultado.fecha_dia_4 = prev_5d?.DailyForecasts?.get(3)?.Date.toString()
resultado.frase_dia_4 = prev_5d?.DailyForecasts?.get(3)?.Day?.ShortPhrase.toString()
resultado.icono_dia_4 = prev_5d?.DailyForecasts?.get(3)?.Day?.Icon!!
resultado.Temp_max_dia_4 = prev_5d?.DailyForecasts?.get(3)?.Temperature?.Maximum?.Value!!
resultado.Temp_min_dia_4 = prev_5d?.DailyForecasts?.get(3)?.Temperature?.Minimum?.Value!!
resultado.fecha_dia_5 = prev_5d?.DailyForecasts?.get(4)?.Date.toString()
resultado.frase_dia_5 = prev_5d?.DailyForecasts?.get(4)?.Day?.ShortPhrase.toString()
resultado.icono_dia_5 = prev_5d?.DailyForecasts?.get(4)?.Day?.Icon!!
resultado.Temp_max_dia_5 = prev_5d?.DailyForecasts?.get(4)?.Temperature?.Maximum?.Value!!
resultado.Temp_min_dia_5 = prev_5d?.DailyForecasts?.get(4)?.Temperature?.Minimum?.Value!!
}
datos_adquiridos = true
Log.println(Log.ASSERT, "datos_tiempo", resultado.toString())
}
}
}
DataModel
data class Datos_Tiempo #RequiresApi(Build.VERSION_CODES.O) constructor(
// Card principal
var ciudad: String? = "Cordoba",
var temperatura_actual: Double? = 18.0,
var temperatura_maxima: Double? = 12.0,
var temperatura_minima: Double? = 6.0,
var unidades_temperatura: String? = "grados",
var icono: Int? = 1,
var icono_frase: String? = "Cloudy",
// Detalles
var presion: Double? = 1020.0,
var direccion_viento: String? = "Sur",
var viento: Double? = 30.0,
var uv: Int? = 4,
var uv_text: String? = "Low",
var humedad_relativa: Double? = 50.0,
#DrawableRes val icon: Int? = R.drawable.ic_1,
val data: String? = "45",
val units: String? = "km/h",
val color: Color? = GreyCard,
//12horas
var hora_1: String = "2022-12-07T12:00:00+01:00",
var icono_hora_1: Int = 6,
var temperatura_hora_1: Double = 13.5,
var temperatura_hora_2: Double = 12.3,
var hora_2: String = "2022-12-07T13:00:00+01:00",
var icono_hora_2: Int = 12,
var temperatura_hora_3: Double = 14.3,
var hora_3: String = "2022-12-07T14:00:00+01:00",
var icono_hora_3: Int = 13,
var temperatura_hora_4: Double = 15.3,
var hora_4: String = "2022-12-07T15:00:00+01:00",
var icono_hora_4: Int = 15,
var temperatura_hora_5: Double = 13.3,
var hora_5: String = "2022-12-07T16:00:00+01:00",
var icono_hora_5: Int = 13,
var temperatura_hora_6: Double = 12.3,
var hora_6: String = "2022-12-07T17:00:00+01:00",
var icono_hora_6: Int = 12,
var temperatura_hora_7: Double = 12.3,
var hora_7: String = "2022-12-07T18:00:00+01:00",
var icono_hora_7: Int = 6,
var temperatura_hora_8: Double = 13.3,
var hora_8: String = "2022-12-07T19:00:00+01:00",
var icono_hora_8: Int = 7,
var temperatura_hora_9: Double = 11.3,
var hora_9: String = "2022-12-07T20:00:00+01:00",
var icono_hora_9: Int = 6,
var temperatura_hora_10: Double = 14.3,
var hora_10: String = "2022-12-07T21:00:00+01:00",
var icono_hora_10: Int = 6,
var temperatura_hora_11: Double = 13.3,
var hora_11: String = "2022-12-07T22:00:00+01:00",
var icono_hora_11: Int = 26,
var temperatura_hora_12: Double = 12.3,
var hora_12: String = "2022-12-07T23:00:00+01:00",
var icono_hora_12: Int = 36,
//5dias
var fecha_dia_1: String = "2022-12-07T18:00:00+01:00",
var frase_dia_1: String = "Mostly Cloudy w/ T-Storms",
var icono_dia_1: Int = 17,
var Temp_max_dia_1: Double = 12.0,
var Temp_min_dia_1: Double = 2.0,
var fecha_dia_2: String = "2022-12-08T18:00:00+01:00",
var frase_dia_2: String = "Rain",
var icono_dia_2: Int = 18,
var Temp_max_dia_2: Double = 15.0,
var Temp_min_dia_2: Double = 4.0,
var fecha_dia_3: String = "2022-12-09T18:00:00+01:00",
var frase_dia_3: String = "Flurries",
var icono_dia_3: Int = 19,
var Temp_max_dia_3: Double = 16.0,
var Temp_min_dia_3: Double = 5.0,
var fecha_dia_4: String = "2022-12-10T18:00:00+01:00",
var frase_dia_4: String = "Mostly Cloudy w/ Flurries",
var icono_dia_4: Int = 20,
var Temp_max_dia_4: Double = 14.0,
var Temp_min_dia_4: Double = 4.0,
var fecha_dia_5: String = "2022-12-11T18:00:00+01:00",
var frase_dia_5: String = "Mostly Cloudy w/ T-Storms",
var icono_dia_5: Int = 16,
var Temp_max_dia_5: Double = 16.0,
var Temp_min_dia_5: Double = 5.0,
)

Two major problems.
First, you are creating your instance of Accesso_API in the Composable, so it is recreated every time things recompose, so you end up with many copies (probably an infinite loop that will eventually crash the device with out-of-memory error), and you are always looking at a new copy who doesn't have any fetched data yet. You need to wrap it in remember { } so it is initialized only once:
val acceso = remember {
Accesso_API()
.apply { coger_tiempo_nombre("Madrid") }
}
Second, you cannot do this kind of thing in UI code:
while (!acceso.datos_adquiridos) {}
This will freeze the whole device because it blocks the main thread.
Compose is meant to be reactive. Don't wait and repeatedly poll values. Create your data property as State that the Composable uses. When the data isn't ready yet, your composable should show a loading indicator or blank screen or something.
So, in your Accesso_API class, change resultado to be MutableState, and you can use null to represent the state when no data is acquired yet:
var resultado: Datos_Tiempo? by mutableStateOf(null)
And completely delete the datos_adquiridos property and any code where you use it.
In your composable, you can get a reference to this state, and you can determine whether it is received yet by checking if it is null.
val resultado = acceso.resultado
if (resultado == null) {
// show screen with loading indicator
return
}
// the rest of your code that builds the screen using non-null resultado.
There could be other problems to fix, but you have so much code I didn't go through it all in detail.
Side note, this kind of repetitive code should be avoided:
//12horas
var hora_1: String = "2022-12-07T12:00:00+01:00",
var icono_hora_1: Int = 6,
var temperatura_hora_1: Double = 13.5,
var temperatura_hora_2: Double = 12.3,
var hora_2: String = "2022-12-07T13:00:00+01:00",
var icono_hora_2: Int = 12,
//...
Use lists or arrays instead of many similar properties! Then you can iterate things and won't have super long functions with repetitive code wherever you work with this data class.

Related

how use memorycache in this method?

how can i add memorycache in this method ?
this is a section of my code that i want to set memory cache on it.
public IActionResult Index(int pageId = 1, string filter = "",
int startPrice = 0, int endPrice = 0, string getType = "", string orderByType = "date",
List<int> selectedGroups = null, List<int> selectedBrand = null, List<int> selectedTags = null
, List<int> selectedsize = null , string Discount = "")
{
ViewBag.selectedGroups = selectedGroups;
ViewBag.selectedTags = selectedTags;
ViewBag.selectedsize = selectedsize;
ViewBag.Discount = Discount;
ViewBag.getType = getType;
ViewBag.Groups = _productService.GetAllGroup();
ViewBag.Tags = _productService.GetTags().Where(c => c.ActiveRow).ToList();
ViewBag.size = _productService.GetSizes().ToList();
ViewBag.pageId = pageId;
return View(_productService.GetProducttype(pageId, filter, startPrice, endPrice, getType, orderByType, selectedGroups, selectedBrand, 24, selectedTags, selectedsize, Discount));
}
private readonly IMemoryCache _memoryCache;
public Constructor (IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
public IActionResult Index(int pageId = 1, string filter = "",
int startPrice = 0, int endPrice = 0, string getType = "", string orderByType = "date",
List<int> selectedGroups = null, List<int> selectedBrand = null, List<int> selectedTags = null
, List<int> selectedsize = null , string Discount = "")
{
ViewBag.selectedGroups = selectedGroups;
ViewBag.selectedTags = selectedTags;
ViewBag.selectedsize = selectedsize;
ViewBag.Discount = Discount;
ViewBag.getType = getType;
var groups = new List<Group>();
if (_memoryCache.TryGetValue("groups", out groups)
{
ViewBag.Groups = groups;
}
else
{
groups = _productService.GetAllGroup();
_memoryCache.Set("groups", groups);
ViewBag.Groups = groups;
}
var tags = new List<Tag>();
if (_memoryCache.TryGetValue("tags", out tags)
{
ViewBag.Tags = tags;
}
else
{
tags = _productService.GetTags().Where(c => c.ActiveRow).ToList();
_memoryCache.Set("tags", tags);
ViewBag.Tags = tags;
}
var sizes = new List<Size>();
if (_memoryCache.TryGetValue("sizes", out sizes)
{
ViewBag.size = sizes;
}
else
{
sizes = _productService.GetSizes().ToList();
_memoryCache.Set("sizes", sizes);
ViewBag.size = sizes;
}
var pageId = null;
if (_memoryCache.TryGetValue("pageId", out pageId))
{
ViewBag.pageId = pageId;
}
else
{
_memoryCache.Set("pageId", pageId);
ViewBag.pageId = pageId;
}
return View(_productService.GetProducttype(pageId, filter, startPrice, endPrice, getType, orderByType, selectedGroups, selectedBrand, 24, selectedTags, selectedsize, Discount));
}

How to switch the position of two buttons randomly؟

I want to switch the positions of the buttons when we press the toggle button. Buttons are repositioned randomly. I tried a lot but was not successful. So if anyone knows how but randomly
val btnOne = findViewById<View>(R.id.buttonOne) as Button
val btnTwo = findViewById<View>(R.id.buttonTwo) as Button
val posOneX = btnOne.x
val posOneY = btnOne.y
val posTwoX = btnTwo.x
val posTwoY = btnTwo.y
btnOne.x = posTwoX
btnOne.y = posTwoY
btnTwo.x = posOneX
btnTwo.y = posOneY
Yes, I found the solution
b4.setOnClickListener {
var arswap = arrayListOf<Button>(b1, b2, b3);
var randomPos: Int = 0;
fun swap(b1: Button, b2: Button) {
var tempPosition = Point(b1.x.toInt(), b1.y.toInt())
b1.x = b2.x;
b1.y = b2.y;
b2.x = tempPosition.x.toFloat();
b2.y = tempPosition.y.toFloat();
}
var totalItems: Int = 3; // total number of items
for (i in 0..totalItems/3 ) {
randomPos = ((Math.random() * arswap.size).toInt());
var randomItem = ((Math.random() * ((totalItems / 2) - 1)) + ((totalItems / 2) + 1));
swap(arswap[randomPos], arswap[randomItem.toInt()])
}
}

NoSuchMethodError : The Setter "movies=" was called on null

Future<YifyMovies> getData() async {
var res = await http.get("https://yts.lt/api/v2/list_movies.json");
var decodedJson = jsonDecode(res.body);
YifyMovies movie = YifyMovies();
movie.data.movies = [];
for (var json in decodedJson) {
movie.data.movies.add(Movies.fromJson(json));
}
return movie;
}
Yify Movies class:-
It's the JSON from the above API converted int0 dart.
class YifyMovies {
String status;
String statusMessage;
Data data;
YifyMovies({this.status, this.statusMessage, this.data});
YifyMovies.fromJson(Map<String, dynamic> json) {
status = json['status'];
statusMessage = json['status_message'];
data = json['data'] != null ? new Data.fromJson(json['data']) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['status'] = this.status;
data['status_message'] = this.statusMessage;
if (this.data != null) {
data['data'] = this.data.toJson();
}
return data;
}
}
class Data {
int movieCount;
int limit;
int pageNumber;
List<Movies> movies;
Data({this.movieCount, this.limit, this.pageNumber, this.movies});
Data.fromJson(Map<String, dynamic> json) {
movieCount = json['movie_count'];
limit = json['limit'];
pageNumber = json['page_number'];
if (json['movies'] != null) {
movies = new List<Movies>();
json['movies'].forEach((v) {
movies.add(new Movies.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['movie_count'] = this.movieCount;
data['limit'] = this.limit;
data['page_number'] = this.pageNumber;
if (this.movies != null) {
data['movies'] = this.movies.map((v) => v.toJson()).toList();
}
return data;
}
}
class Movies {
int id;
String url;
String imdbCode;
String title;
String titleEnglish;
String titleLong;
String slug;
int year;
double rating;
int runtime;
List<String> genres;
String summary;
String descriptionFull;
String synopsis;
String ytTrailerCode;
String language;
String mpaRating;
String backgroundImage;
String backgroundImageOriginal;
String smallCoverImage;
String mediumCoverImage;
String largeCoverImage;
String state;
List<Torrents> torrents;
String dateUploaded;
int dateUploadedUnix;
Movies(
{this.id,
this.url,
this.imdbCode,
this.title,
this.titleEnglish,
this.titleLong,
this.slug,
this.year,
this.rating,
this.runtime,
this.genres,
this.summary,
this.descriptionFull,
this.synopsis,
this.ytTrailerCode,
this.language,
this.mpaRating,
this.backgroundImage,
this.backgroundImageOriginal,
this.smallCoverImage,
this.mediumCoverImage,
this.largeCoverImage,
this.state,
this.torrents,
this.dateUploaded,
this.dateUploadedUnix});
Movies.fromJson(Map<String, dynamic> json) {
id = json['id'];
url = json['url'];
imdbCode = json['imdb_code'];
title = json['title'];
titleEnglish = json['title_english'];
titleLong = json['title_long'];
slug = json['slug'];
year = json['year'];
rating = json['rating'];
runtime = json['runtime'];
genres = json['genres'].cast<String>();
summary = json['summary'];
descriptionFull = json['description_full'];
synopsis = json['synopsis'];
ytTrailerCode = json['yt_trailer_code'];
language = json['language'];
mpaRating = json['mpa_rating'];
backgroundImage = json['background_image'];
backgroundImageOriginal = json['background_image_original'];
smallCoverImage = json['small_cover_image'];
mediumCoverImage = json['medium_cover_image'];
largeCoverImage = json['large_cover_image'];
state = json['state'];
if (json['torrents'] != null) {
torrents = new List<Torrents>();
json['torrents'].forEach((v) {
torrents.add(new Torrents.fromJson(v));
});
}
dateUploaded = json['date_uploaded'];
dateUploadedUnix = json['date_uploaded_unix'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['url'] = this.url;
data['imdb_code'] = this.imdbCode;
data['title'] = this.title;
data['title_english'] = this.titleEnglish;
data['title_long'] = this.titleLong;
data['slug'] = this.slug;
data['year'] = this.year;
data['rating'] = this.rating;
data['runtime'] = this.runtime;
data['genres'] = this.genres;
data['summary'] = this.summary;
data['description_full'] = this.descriptionFull;
data['synopsis'] = this.synopsis;
data['yt_trailer_code'] = this.ytTrailerCode;
data['language'] = this.language;
data['mpa_rating'] = this.mpaRating;
data['background_image'] = this.backgroundImage;
data['background_image_original'] = this.backgroundImageOriginal;
data['small_cover_image'] = this.smallCoverImage;
data['medium_cover_image'] = this.mediumCoverImage;
data['large_cover_image'] = this.largeCoverImage;
data['state'] = this.state;
if (this.torrents != null) {
data['torrents'] = this.torrents.map((v) => v.toJson()).toList();
}
data['date_uploaded'] = this.dateUploaded;
data['date_uploaded_unix'] = this.dateUploadedUnix;
return data;
}
}
class Torrents {
String url;
String hash;
String quality;
String type;
int seeds;
int peers;
String size;
int sizeBytes;
String dateUploaded;
int dateUploadedUnix;
Torrents(
{this.url,
this.hash,
this.quality,
this.type,
this.seeds,
this.peers,
this.size,
this.sizeBytes,
this.dateUploaded,
this.dateUploadedUnix});
Torrents.fromJson(Map<String, dynamic> json) {
url = json['url'];
hash = json['hash'];
quality = json['quality'];
type = json['type'];
seeds = json['seeds'];
peers = json['peers'];
size = json['size'];
sizeBytes = json['size_bytes'];
dateUploaded = json['date_uploaded'];
dateUploadedUnix = json['date_uploaded_unix'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['url'] = this.url;
data['hash'] = this.hash;
data['quality'] = this.quality;
data['type'] = this.type;
data['seeds'] = this.seeds;
data['peers'] = this.peers;
data['size'] = this.size;
data['size_bytes'] = this.sizeBytes;
data['date_uploaded'] = this.dateUploaded;
data['date_uploaded_unix'] = this.dateUploadedUnix;
return data;
}
}
Any help, please? Tell me if any more info is required?
I'm trying to list all the movies and use them for tabview in flutter which can show them in listview as well as gridview. I'm using Bloc architecture for layouts so YifyMovie class is in hom_model and the code above is in home_provider.
Your YifyMovies class is already handling the parsing for you.
So your code should change from
Future<YifyMovies> getData() async {
var res = await http.get("https://yts.lt/api/v2/list_movies.json");
var decodedJson = jsonDecode(res.body);
YifyMovies movie = YifyMovies();
movie.data.movies = [];
for (var json in decodedJson) {
movie.data.movies.add(Movies.fromJson(json));
}
return movie;
}
to
Future<YifyMovies> getData() async {
var res = await http.get("https://yts.lt/api/v2/list_movies.json");
var decodedJson = jsonDecode(res.body);
YifyMovies movie = YifyMovies.fromJson(decodedJson);
return movie;
}

How to duplicate line on itself?

In a JetBrains editor, what is the shortcut (or trick) to duplicate a line on itself without breakline (not like Ctrl + D). In other words, I want to turn this:
constructor(obj: Schema$Car) {
this.brand =
this.id =
this.model =
this.photoBack =
this.photoFront =
this.photoInteriorBack =
this.photoInteriorFront =
this.photoSide =
this.seatCount =
this.year =
}
into this:
constructor(obj: Schema$Car) {
this.brand = obj.brand
this.id = obj.id
this.model = obj.model
this.photoBack = obj.photoBack
this.photoFront = obj.photoFront
this.photoInteriorBack = obj.photoInteriorFront
this.photoInteriorFront = obj.photoInteriorFront
this.photoSide = obj.photoSide
this.seatCount = obj.seatCount
this.year = obj.year
}

Contact Test Bit Mask not working proper

contactTestBitMask works in general but if the graphic is too complex contactTestBitMask freaks out and you have only a small contact area or get weird errors. What can i do there?
This is an image about my area:
class GameScene: SKScene, SKPhysicsContactDelegate {
let WallCategory : UInt32 = 0x1 << 1
let ShipCategory : UInt32 = 0x1 << 2
var counter = 0
var userCanBuild1Building = false
var KameraSichtVar:SKCameraNode = SKCameraNode()
var touched:Bool = false
var startTouch = CGPointMake(0, 0)
//------------------------------
var draufgeklickt:CGPoint = CGPoint()
var draufgeklicktUpdate:CGPoint = CGPoint()
var userWantScroll = false
var userWantBuild = false
let wohnhausBlue = SKSpriteNode(imageNamed:"wohnhausBlue")
let buildMenu = SKSpriteNode(imageNamed:"Buildmenu")
var hitDetected = false
let actualBuilding = SKSpriteNode()
var userWantUpgrade : Bool = false
//var theBuildingBefore = SKSpriteNode()
//var houseLvlSave : Int = 0
var uphausSave : String = ""
func didBeginContact(contact: SKPhysicsContact) {
var BuildingObject: SKPhysicsBody
var ClickObject: SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask
{
BuildingObject = contact.bodyA
ClickObject = contact.bodyB
}
else
{
BuildingObject = contact.bodyB
ClickObject = contact.bodyA
}
// upgrade Building
let name = BuildingObject.node!.name
let HouseLvL: Int = Int(name!)!
if HouseLvL > 0 && userCanBuild1Building == true{
userCanBuild1Building = false
let touchedBuilding = SKSpriteNode(imageNamed: BuildingObject.node!.name!)
touchedBuilding.name = BuildingObject.node!.name
let name = BuildingObject.node!.name
var HouseLvL: Int = Int(name!)!
HouseLvL = HouseLvL + 1
let uphaus = String(HouseLvL)
let setSpriteTexture = SKAction.setTexture(SKTexture(imageNamed: uphaus))
let upSize = SKSpriteNode(imageNamed: uphaus)
let resizeHousewidth = SKAction.resizeToWidth(upSize.size.width, duration: 0)
let resizeHouseheight = SKAction.resizeToHeight(upSize.size.height, duration: 0)
touchedBuilding.runAction(resizeHousewidth)
touchedBuilding.runAction(resizeHouseheight)
touchedBuilding.runAction(setSpriteTexture)
touchedBuilding.name = "\(uphaus)"
touchedBuilding.position = BuildingObject.node!.position
touchedBuilding.zPosition = BuildingObject.node!.zPosition
touchedBuilding.userInteractionEnabled = false
let upgradedbuilding = SKSpriteNode(imageNamed: uphaus)
touchedBuilding.size = CGSizeMake(upgradedbuilding.size.width, upgradedbuilding.size.height)
touchedBuilding.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed:"\(uphaus)"), size: upgradedbuilding.size)
touchedBuilding.physicsBody?.categoryBitMask = WallCategory
touchedBuilding.physicsBody?.affectedByGravity = false
touchedBuilding.physicsBody?.dynamic = true
touchedBuilding.physicsBody?.collisionBitMask = 0
addChild(touchedBuilding)
BuildingObject.node?.removeFromParent()
ClickObject.node?.removeFromParent()
}
if HouseLvL == 0
{
ClickObject.node?.removeFromParent()
}
}`override func didMoveToView(view: SKView) {
/* Setup your scene here */
if let KameraSicht:SKCameraNode = self.childNodeWithName("Kamera") as? SKCameraNode{
KameraSichtVar = KameraSicht
self.camera = KameraSichtVar
}
physicsWorld.contactDelegate = self
self.physicsWorld.gravity = CGVectorMake(0, 0)
func addBG(){
let background = SKSpriteNode(imageNamed: "0")
background.name = "0"
background.anchorPoint = CGPointMake(0.5, 0.5)
background.zPosition = 2
background.position = CGPointMake(CGRectGetMinX(self.frame)+self.frame.width/4, CGRectGetMaxY(self.frame)-self.frame.height/4)
background.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "0"), size: background.size)
//background.physicsBody = SKPhysicsBody(rectangleOfSize: background.size)
background.physicsBody?.categoryBitMask = WallCategory
background.physicsBody?.affectedByGravity = false
background.physicsBody?.dynamic = true
background.physicsBody?.collisionBitMask = 0
self.addChild(background)
}
addBG()
let wall = SKSpriteNode(imageNamed:"1" )
wall.name = "1"
wall.zPosition = 10
//wall.xScale = 3
//wall.yScale = 3
wall.size = CGSizeMake(100, 100)
wall.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
wall.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: wall.name!), size: wall.size)
//wall.physicsBody = SKPhysicsBody(rectangleOfSize: wall.size)
wall.physicsBody?.affectedByGravity = false
wall.physicsBody?.dynamic = false
wall.physicsBody!.categoryBitMask = WallCategory
wall.physicsBody?.collisionBitMask = 0
self.addChild(wall)
}
`
ok i found the solution. the problem was the weird behavior of the contactTestBitMask when you use this graphic.(it only detected a contact in a small area of the graphic). the reason for that is that the graphic have separated shapes (like the half ring and some other small stuff).
i connected all shape so that its only one shape and now everything works perfectly fine. also tried it with some other graphics and the behavior of contactTestBitMask is: if you have separated shapes in your graphic, contactTestBitMask take exactly one of the shapes and make it the contact area ...but the other shapes in your graphic will not detect a contact then...