visit
This will be a continuation of the article The Evolution of MV* Patterns in Android: Part 2
sealed interface Event {
object FetchBooks: Event
}
sealed interface State {
object Loading: State
object Empty: State
data class Success(val books: List<Book> = emptyList()) : State
data class Error(val errorMessage: String)
}
Let's move on to the ViewModel. Here we will also use LiveData
, but keep in mind that you can use Kotlin Flow
.
class MainViewModel : ViewModel() {
private val booksRepository: BooksRepository = BooksRepositoryImpl()
private val _state = MutableLiveData<State>(State.Empty)
val state: LiveData<State>
get() = _state
fun event(event: Event) {
_state.value = State.Loading
when(event) {
Event.FetchBooks -> {
booksRepository.fetchBooks {
if (it.isNotEmpty())
_state.value = State.Success(books = it)
else
_state.value = State.Empty
}
}
}
}
}
class MainFragment : Fragment(R.layout.fragment_main) {
private lateinit var booksRV: RecyclerView
private lateinit var progressBar: ProgressBar
private val adapter = BooksListAdapter()
private val mainViewModel: MainViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
booksRV = view.findViewById(R.id.books_rv)
progressBar = view.findViewById(R.id.progress_bar)
mainViewModel.event(Event.FetchBooks)
observeLiveData()
}
private fun observeLiveData() {
mainViewModel.state.observe(viewLifecycleOwner) { state ->
when(state) {
is State.Empty -> {
progressBar.visibility = View.GONE
}
is State.Loading -> {
progressBar.visibility = View.VISIBLE
}
is State.Success -> {
progressBar.visibility = View.GONE
adapter.submitList(state.books)
}
is State.Error -> {
Toast.makeText(context, state.errorMessage, Toast.LENGTH_SHORT).show()
}
}
}
}
companion object {
@JvmStatic
fun newInstance() = MainFragment()
}
}
Pros
Cons
State
become huge and we might want split this State
into smaller ones with extra StateFlows
instead of just using one.