Android MVI with Jetpack Compose
This year, at Google I/O 2019, JetPack Compose has been announced. It is still
in an early-exploration, pre-alpha stage.
as stated in the official documentation page. And after digging a little into the demo-preview (which you can download) I completely agree with that statement. It’s missing some basic component and it’s not ready for production nor for simply migrating some part of a perk project.
But that doesn’t really matter. I’m more than happy with what we got so far. I had the opportunity to try it out a little and check the potentials of this new way of building apps’ UI.
In my opinion, starting with Flutter and then with this library, Google’s trying to port the web-development way into mobile applications. In Flutter everything is a Widget
, here instead the components are declared as functions.
This might be a really good start, and will also help to learn Flutter for sure. Once you mastered the idea of building UIs through “composing” components, switching to the Flutter (React) pattern is pretty simple. The paradigm behind building component is not so far away between the two.
Brief intro
JetPack Compose allows you to declare
UI components, including drawing and creating custom layouts.
This mean you could have a function which returns nothing — literally, the function has no return type:
But this function, thanks to the@Composable
annotation, allows us to declare a “view” which can be set (it’s drawn actually) via setContent()
of the activities.
This is a method that initializes the composable widget tree and wraps it in a FrameLayout
.
The usual way
What I’m planning to show you, is how we could implement MVI pattern using this new Jetpack Compose in a fancy way.
Right now, we usually do something like this to handle MVI in Android:
We publish through LiveData<MVIViewState>
whatever we fetch from a remote or from local storage (cache that data!) this way:
Then we observe that LiveData
from our activity:
The View
is responsible to render the UI entirely, which sometimes leads to use logic in the View
as well, if-else statements to display or hide something, and that when
block, is really frustrating, isn’t it? Why should the View
check the state, and render a state in a different way from another one? The View
should receive a State
and just render it, without performing anything else (in a pure functional way).
Let’s change how we build things!
What if we can change this situation, giving the ViewModel
, or way better — the ViewState
— the power to build the UI, and letting the View
only render it? That’s possible thanks to Jetpack Compose.
So, what do we need to change?
The MVIViewState
class will be a little bit different:
This class is pretty verbose, and there’s a reason for it:ViewState
should represent the View
, so it should know how to render it, right?
By adding a
function to the ViewState
we force every subclass to define it, that means that every child-class will know how to “render” its state. In this way, inside the ViewState
class, we build our View
accordingly to the ViewState
. This will allow us to have a leaner View
(Activity or Fragment).
So, what about the ViewModel
class or the Activity/Fragment
? Well, the ViewModel
basically remains the same:
Okok, and the View
?
HERE it is! Isn’t it beautiful?
I personally love how the view just became really lean and basically doesn’t do anything. I̶t̶ ̶o̶b̶s̶e̶r̶v̶e̶s̶ ̶t̶h̶e̶ ̶L̶i̶v̶e̶D̶a̶t̶a̶ ̶a̶n̶d̶ ̶t̶h̶a̶t̶’̶s̶ ̶i̶t̶.̶ ̶T̶h̶r̶o̶u̶g̶h̶ ̶t̶h̶e̶ ̶s̶e̶t̶C̶o̶n̶t̶e̶n̶t̶ ̶{̶}̶ ̶f̶u̶n̶c̶t̶i̶o̶n̶ ̶f̶r̶o̶m̶ ̶t̶h̶e̶ ̶J̶e̶t̶p̶a̶c̶k̶C̶o̶m̶p̶o̶s̶e̶ ̶l̶i̶b̶r̶a̶r̶y̶,̶ ̶w̶e̶’̶r̶e̶ ̶n̶o̶w̶ ̶a̶b̶l̶e̶ ̶t̶o̶ ̶d̶o̶ ̶t̶h̶i̶s̶ ̶m̶a̶g̶i̶c̶ ̶t̶r̶i̶c̶k̶!̶
EDIT: thanks to Leland Richardson and his suggestions the View
is now even better.
Instead of calling setContent{}
several times, it creates the basic component of the view: CraneWrapper
, Scaffold
with AppBar
and then render inside the Scaffold
the part of the View
which is the right one to render by observing changes on the ViewState
.
Downsides
There are some downsides though:
- Views are not responsible anymore of building the layout
- There’s no XML file, so you have to find
ViewState
class, search for the right state you want to inspect, and read, one by one, each component which it builds - Layout Inspector does not work. WAIT, WHAT?!? Yes, it doesn't. That’s because Jetpack Compose does not really create views in the view hierarchy, it just draws on canvas, so basically you’re inspecting a
Bitmap
.
Do we care?
Not at all. Let me explain myself. Those downsides listed above are real downsides. I mean, I would be stuck trying to find out why a click on a component is not working since December if it wouldn’t be for the Layout Inspector.
But since the View
is not responsible anymore to build the layout, you can easily check how it’s built. You won’t have any more hidden loaders, hidden recyclers, hidden custom views. All your user needs to see, will be built into the “View hierarchy” and drawn on the canvas. You’ll end up having a WYSIWYG.
Conclusions
That’s it! This was just a brief introduction to what I expect it will be the development of Jetpack Compose combined with the MVI pattern in the Android world!
Hope you enjoyed the reading! Every feedback is appreciated (be nice, that’s my first blog post :D).