https://kotlinlang.org logo
#compose
Title
# compose
r

Rafa Gómez

03/03/2024, 7:08 PM
Hey everyone, I'm learning about kotlin multiplatform and compose multiplatform by doing a super simple mobile app (I have a kotlin backend background). I have the following view model (from moko mvvm library)
Copy code
class AgendaViewModel(
    api: AgendaAPI
) : ViewModel() {

    private val searchAgenda = AgendaSearcher(api)
    private val bookAgenda = AgendaBooker(api)
    private val cancelAgenda = AgendaCanceler(api)

    private val internalState = MutableStateFlow(AgendaViewModelState())

    init {
        // Perform initial search operation to populate the state
        viewModelScope.launch {
            val initialAgendas = searchAgenda.invoke2(9, 24)
            internalState.update { it.copy(agendas = initialAgendas) }
        }
    }

    val state = combine(
        internalState,
        searchAgenda(9, 24)
    ) { state, agendas -> state.copy(agendas = agendas) }
        .stateIn(viewModelScope, WhileSubscribed(5000L), AgendaViewModelState())
}
Inside my state there's a list of agendas I'd like to get from my backend and use it as the initial state of my view model. I access the backend through a ktor client and using the following interface
Copy code
interface AgendaAPI {
    fun search(criteria: SearchAgendaCriteria): Flow<List<Agenda>>
    suspend fun searchV2(criteria: SearchAgendaCriteria): List<Agenda>
}
I've tried both options (flow and suspend with direct result), the block inside the init method returns an initial agenda with 7 values but then it's not updated as a state. Whenever I create the view model from the app and call the composable it's still an empty list of agendas.
Copy code
@Composable
fun App() {
    initKoin()

    val mainScope = MainScope()

    val restClientClient = RestClient()
    val agendaClient = RestAgendaClient(restClientClient)
    val api = RestAgendaAPI(agendaClient)

    MaterialTheme {
        val viewModel = getViewModel(
            key = "agendas-view-screen",
            factory = viewModelFactory {
                AgendaViewModel(api)
            }
        )
        val state by viewModel.state.collectAsState()
        AgendaListScreen(state, viewModel::onEvent)
    }
}
I'm pretty sure I'm doing something wrong here, does anyone know what is it? Any other improvements or tips are also welcomed. Not 100% sure if this is the right place though but I'd highly appreciate the any help.
🧵 2
s

Sean Proctor

03/03/2024, 9:59 PM
Try feeding in known good data instead of trying to pull from your backend. The
init
seems pretty useless currently because it just updates a value that is never used (since your
agendas
is always set by to the result from
searchAgenda(9, 24)
in the
combine
. I would create your public state like
val state: StateFlow<AgendaViewModelState> = internalState
since you already have a backing
StateFlow
that you're updating.
4 Views