This weblog collection covers a “widespread UI first” migration technique of a View pattern to Jetpack Compose, guiding you thru the steps, with interop and type issue help, and why you may contemplate an analogous method to your Compose migration.
Welcome again to our Compose migration journey 🚢 ! Partly 1️⃣, we coated:
- The intro and objectives of this migration
- Temporary overview of the migration pattern
- First three steps of the captain’s log: 1. migration prep, 2. dependencies & theming, 3. migration of smallest widespread UI
Let’s proceed onto new adventures!
Step 0: Migration prep [in part 1]
Step 1: Dependencies and theming [in part 1]
Step 2: Smallest widespread UI elements [in part 1]
Step 3: Migration of extra advanced elements [in this post]
Step 4: Migration of low threat screens [in this post]
Step 5: Migration of extra advanced screens [in this post]
Step 3: Migration of extra advanced elements
🗒️ Duties:
- Discover advanced components
- Migrate components
Discover advanced components
One of many largest benefits of Compose, in my completely unbiased opinion 🙄, are Lazy layouts and their simplicity in comparison with RecyclerView
. Subsequently, this step rightly goals to take away as a lot RecyclerView
-related code as doable and exchange it with Compose. For an in depth step-by-step doc, confer with Migrate RecyclerView to Lazy listing migration information.
Our first goal is the e-mail attachment grid.
Migrate components
We begin by changing EmailFragment’
sRecyclerView
XML with a ComposeView
, retaining its positioning constraints as is:
All different attributes, like paddings, and many others., can be outlined in its composable, as we need to make it customizable. We then discover the place the earlier RecyclerView
binding was used, within the EmailFragment
, and add:
That’s all it takes. No ViewHolder
s, no Adapter
s — an enormous quantity of code eliminated by migrating only one part.
The earlier EmailAttachmentGridAdapter
had advanced, customized logic to simulate a staggered structure, which we’ve merely changed with a LazyVerticalStaggeredGrid
. This API has a really cool manner of defining a dynamic and adaptive measurement of your grid, by setting a minimal measurement for an merchandise and letting the grid match as many columns or rows as it could within the given house. That is good for supporting completely different type components and an amazing adaptive consumer expertise:
Within the PR, we additionally exchange one other RecyclerView
with EmailAttachmentRow
, however we’ll ⏩ that.
Now get pleasure from eradicating complete lessons similar to EmailAttachmentAdapter
and EmailAttachmentViewHolder
and different layouts 😈.
💻 PR: Step 3
🏅 MVP participant: Association
API in Lazy layouts. Its capabilities make the customization of lists and grids in Compose even simpler. For extra visuals, try the MAD Abilities episode — Fundamentals of Compose Layouts and Modifiers.
💎 Professional tip: As an alternative of a grid, you might additionally use a Move structure, relying in your designs. Or write your individual customized structure. As much as you. The fantastic thing about Compose is which you could simply obtain one design in a number of other ways.
Step 4: Migration of low threat screens
🗒️ Duties:
- Discover the very best display screen candidates
- Migrate screens
Discover the very best display screen candidates
Now, nearly all fragments have a little bit of Compose code, and we’re capable of transfer onto screen-level migration.
Do not forget that you don’t have to migrate previous code to Compose — you’ll be able to simply use it for all new options and screens onward. Nonetheless, if you’ll be able to totally migrate, it is best to realize it brings further advantages:
- Much less UI code and information to dig by way of
- Upkeep of a singular, declarative UI framework
- Avoiding context switching between Views and Compose
- Less complicated and simpler APIs to write down and use, like animations and Lazy layouts
- Simplifying testing and navigation through the use of Compose-only, avoiding interop
Since Reply isn’t getting any new options anytime quickly, we select to refactor present screens.
On screen-level, there are completely different tiers of type issue help. Some require main design adjustments, like implementing list-detail or different canonical layouts, whereas some require a primary help, like not being letterboxed.
Goal screens that don’t require main structure adjustments and are low-risk emigrate first to Compose.
Migrate screens
Search display screen was the right candidate, as it’s a low-engagement, low-complexity characteristic.
Since all particular person gadgets had been already transformed, migrating this display screen was a bit of cake 🍰:
At screen-level, we add system multipreviews to make sure our composables look nice on all display screen sizes:
SearchFragment
UI now consists of a SearchScreen
composable. We maintain the fragment solely as an outer wrapper to proceed utilizing the present navigation framework and transitions, following the rules of the Navigation migration information, however we will take away any previous XML information and sources.
💻 PR: Step 4
🏅 MVP participant: Window insets and corresponding modifiers like systemBarsPadding
. These are vital at screen-level, to make sure your app and the system UI collaborate effectively and don’t get in one another’s manner:
💎 Professional tip: If there’s a View part too sophisticated emigrate but, and is obstructing you, wrap it in an AndroidView
and maintain utilizing it in Compose. Make it an issue to your future self 😁.
Step 5: Migration of extra advanced screens
🗒️ Duties:
- Discover the very best display screen candidates
- Design for type components at screen-level
- Migrate screens individually
- Pair into list-detail canonical structure
Discover the very best display screen candidates
Our earlier step was taking a look at low-risk/low-engagement screens, with minimal to no structure adjustments required for supporting type components.
However in each migration journey, there’ll come a time for these riskier and extra advanced screens. Worry not, there’s a protected manner of doing this.
It is a nice time to contain your designer and rethink some screens with a type issue mindset. The performance and UI of your classical telephone show doesn’t want to vary, and also you’ll have the ability to leverage the ability of Compose to construct reusable, adjustable composables and mix them in a different way primarily based on the display screen measurement.
Canonical layouts play a significant position in offering the very best consumer expertise, as they contemplate widespread use circumstances for a way apps adapt visually to roughly house. Analyze your app and flag screens which may very well be a superb match for Feed, Record-detail, or Supporting pane.
In Reply, the canonical use circumstances are Dwelling and E mail fragments, as they match the list-detail structure completely:
Design for type components at screen-level
To set the fitting mindset, use this form issue cheatsheet for screen-level composables:
- Begin with low-risk screens that require least design adjustments, and scale to high-risk, extra advanced screens
- Make choices primarily based on the house that’s allotted to your screens, and never mounted, {hardware} values
- When given an quantity of display screen house, take into consideration how one can greatest use it to show content material to the consumer. Might you utilize canonical layouts or realign some components in a different way, primarily based on the display screen measurement?
- Make sure the app and screens are constant throughout configuration adjustments like system rotation, by sustaining the scroll place, a logical navigation vacation spot, and related seen data
- Use
WindowSize
lessons to outline completely different content material sorts and switch them into observable state - Go this state solely to screen-level composables. For lower-level, you shouldn’t use the present window metrics instantly
- Depend on adaptive instruments similar to: Grid’s adaptive and span APIs, Window insets, canonical layouts, Move layouts, previews and multipreviews for all display screen sizes, resizable emulator, scrollable modifiers,
fillMax…
modifiers,weight
s, adaptiveAssociation
s for lists and grids, and Accompanist adaptive utils
Migrate screens individually
We begin by migrating Dwelling and E mail fragment’s UI to Compose HomeScreen
and EmailScreen
, after which pair them up in a Dwelling and E mail list-detail. Writing composable replicas of EmailFragment
and HomeFragment
is fairly simple, so we’ll ⏩ that.
We use Window Measurement lessons to assist convert the uncooked display screen sizes into significant measurement lessons and group them into buckets:
This data ought to solely be handed all the way down to screen-level composables. For decrease ranges, we shouldn’t use the present window metrics instantly or by way of CompositionLocal
, however fairly use the house that the composable is given to render itself.
We cross the dimensions class down as observable state to the “host” of the list-detail structure, which is the HomeFragment
and its corresponding HomeScreen
composable UI:
Pair into list-detail canonical structure
Each time the window measurement class is Expanded
and the content material kind is TWO_PANE
, we wish the Dwelling display screen host to point out the E mail listing and E mail element in list-detail canonical structure:
Or else, we wish the E mail listing and E mail screens separate in their very own fragments, because it was initially:
Right here, we use a useful shortcut to shortly arrange a list-detail structure and supply the technique on how the 2 panes needs to be laid out: the TwoPane structure from Accompanist. Nonetheless, you might additionally write your individual list-detail, following the JetNews pattern or the canonical layouts repository.
To make sure the list-detail appears to be like good, we once more use the multipreview for a candy combo of theme and system previews:
To supply the very best consumer expertise, we need to maintain the similar scroll place of our E mail listing when switching between single and two pane (for instance, when rotating a pill system):
Since HomeScreen
decides which mode to show, we hoist a singular scrollState
and share it between the E mail listing alone and E mail listing in TwoPane
:
So, this was all HomeScreen
and its fragment to this point — what concerning the EmailScreen
and its fragment?
At first of this step, we migrated each HomeFragment
and EmailFragment
UI into Compose HomeScreen
and EmailScreen
, and saved the fragments as outer wrappers for navigation. HomeScreen
now hosts two use circumstances — Dwelling with e-mail listing and Dwelling with e-mail listing + e-mail element.
If the Dwelling e-mail listing is in single pane and we faucet on an e-mail, we nonetheless have to deal with navigating to EmailFragment
and displaying EmailScreen
standalone, as earlier than the migration:
In a Compose-only app, this is able to be dealt with by Navigation Compose because the greatest solution to navigate between completely composable locations. However in our interop pattern, we’re retaining Jetpack Navigation and, due to this fact, require fragments as locations.
The EmailScreen
element wants to stay a separate EmailFragment
vacation spot, in order that it may be navigated to from both HomeFragment
or from another level within the app.
Fortunately, we extracted the EmailScreen
composable, so we simply share the UI between these two eventualities:
onEmailClick
in HomeScreen
is completely different relying on whether or not we need to carry out an precise navigation motion from Dwelling to E mail in single pane, or simply replace the already seen E mail composable with new e-mail information in TwoPane
(confer with earlier pictures).
Observe: On the time of scripting this weblog, Shared aspect transitions are nonetheless not doable, so we weren’t capable of migrate aspect transitions.
💻 PR: Step 5
🏅 MVP participant: NestedScrollConnection
. Reply has a View BottomAppBar
set within the activity_main.xml
which “surrounds” all the app and all of our nested composable screens, and collapses on scroll:
Now that Dwelling UI is in Compose, how will we preserve the collapsible interop between the View BottomAppBar
and the HomeScreen
composable? It’s really so easy it appears to be like like magic.
Simply add rememberNestedScrollInteropConnection
to the scrollable composable and it connects the whole lot behind the scenes:
💎 Professional tip: DisplayFeature
gives an outline of a bodily characteristic on the show. For instance, the FoldingFeature
describes a fold within the versatile show or a hinge between two bodily show panels. It really works equally to WindowSize
lessons and is supplied to TwoPane
:
And with this, we conclude our migration course of!
- Migrate safely and incrementally — the code you’re feeling snug (or simply barely uncomfortable!) migrating. Keep in mind, your app doesn’t need to be totally 100% Compose to reap its advantages
- Not everybody must be a Compose knowledgeable to begin writing Compose code. You possibly can study it on the go, whereas writing and reviewing. The fantastic thing about Compose is which you could obtain one design in a number of methods, all doubtlessly equally “right”. There’s an intensive set of supplies to begin your studying course of, however within the meantime one of the best ways of studying is by doing!
- Have enjoyable! I typically overlook this myself. You get caught up in deadlines, design necessities and a rising pile of Jira tickets, and also you overlook that we’re in Android growth as a result of (presumably) we love making apps. Having enjoyable whereas doing so, even when it’s a part of our work, needs to be necessary! Compose contributes so much to this enjoyable expertise.
- Final however not least, get pleasure from eradicating quite a few XMLs, sources and large strains of View code 😈
When you get extra snug with Compose, migrating screens and elements turns into quicker, enjoyable and rewarding, lowering the quantity of code and enhancing readability.
However it may be a frightening job for bigger codebases. That’s why refactoring previous code isn’t a necessity and you should utilize Compose just for constructing new options and screens.
To allow this, contemplate migrating widespread UI and design techniques first, however make an knowledgeable choice on whether or not to maintain the View elements in parallel or not.
Moreover, to ease the migration workload, you’ll be able to maintain utilizing your unique navigation framework and solely take into consideration migrating to Navigation Compose as soon as all of your screens are in Compose.
In the event you’re redesigning screens or options, contemplate taking this chance to undertake Compose, as you’ll have to do some rewriting in any case. And this opens up extra potentialities of introducing type issue help alongside the best way.
Keep tuned for the sequel!
Migrating to Jetpack Compose — an interop love story [part 1]
Migrating to Jetpack Compose — an interop love story [part 2]