Customized Jetpack Compose format with Kotlin DSL | by Evgeny Landarsky | Dec, 2022


Though Jetpack Compose supplies a variety of parts for creating a gorgeous UI, at some point chances are you’ll discover that your following format must be applied with a customized part. Jetpack Compose for instance doesn’t have a ready-to-use handy part to work with grid-based UI. What we perceive by comfort:

  • versatile chance to outline row and column sizes
  • сontent placement in any specified location with out the necessity to place neighbors

We wish to have a part that enables us to create UI like within the following picture:

In Jetpack Compose we have already got comparable parts: LazyVerticalGrid and LazyHorizontalGrid. Here’s a frequent utilization of LazyHorizontalGrid:

Look shut however not the identical as what we’d like. Lazy grids function solely with rows and columns depend and place objects sequentially. Whereas we wish to management not solely counts however sizes. As well as, we have to have the chance to put an merchandise in any place.

Primarily based on the lazy grids API, we’ll outline our part API and name it GridPad:

As a result of we wish to management content material placement our part will handle so as to add objects with Kotlin DSL through merchandise sections.

Let’s take a better take a look at GridPadCells. This class accommodates details about row and column sizes.

Let’s go a little bit again and bear in mind what we’d like. We have to have the chance to specify a measurement for every row and column in a grid.

Due to this fact, GridPadCells cowl all our necessities. You may discover, for storing rowSizes and columnSizes utilizing ImmutableList. Because of this implementation of a listing, the compose compiler marks this class as steady and we don’t must mark GridPadCells with @Immutable or @Secure annotation. Why it will be significant? Right here’s what the official documentation says:

Not all courses should be steady, however a category being steady unlocks plenty of flexibility for the compose compiler to make optimizations when a steady kind is being utilized in locations, which is why it’s such an necessary idea for compose.

Right here is a superb article that explains in additional element Jetpack Compose core ideas comparable to stability, skippability, and restartability. I like to recommend studying it to higher perceive the essential ideas of Jetpack Compose and what they’ll have an effect on.

There are two extra courses that we’ve not coated but: GridPadCellSize and TotalSize. The GridPadCellSize is only a container that accommodates details about the scale of a selected row or column:

The TotalSize is a helper class that accommodates details about the sum of all sorts of sizes for a row or column and is utilized in a measuring stage:

And the ultimate class here’s a builder class. This class is just not essential, but it surely helps to cut back boilerplate code. Our GridPad requires two lists of sizes, which within the fundamental use case would be the similar. For that cause, we should always create a mutable class with default initialization and with the flexibility to override chosen sizes:

The second main objective that we wish to obtain is placement content material in any cell on the grid with any span width and peak.

Few necessary edge circumstances that we’ll deal with:

  • a cell could comprise multiple component
  • the content material could overlap with one another
  • content material that goes outdoors the grid can be ignored

Right here is the signature of GridPad:

Let’s have a look intimately at GridPadScope. GridPadScope is the DSL scope that limits what customers can put into our composable layouts. Solely content material with the merchandise are acceptable:

Appears to be like fairly straightforward on the highest however is a little bit bit difficult within the particulars. Right here is GridPadScope definition:

Listed here are two significant issues: @GridPadScopeMarker and GridPadItemScope. The @GridPadScopeMarke is a @DslMarker:

The GridPadItemScope is a context receiver:

The above mixture helps to make strict use of our API: a developer can’t place different composables with out wrapping them into an merchandise. Nor can the developer put an merchandise inside one other merchandise. If you need to be taught extra about context receivers I like to recommend you learn this text.

As you may discover, the whole lot that we’ve simply explored is an interface. Let’s have a look intimately at implementations. Earlier than shifting on, bear in mind what the composable lifecycle appears like. For customized format, we have to implement three sub-stages of the format stage:

Thus we have to measure the youngsters, decide the scale of the format and place the content material. Here’s a very top-level of the GridPad implementation:

The one interface definition isn’t sufficient to implement the logic of including content material. For that cause, we have to have some implementation, and that implementation is GridPadScopeImpl. Right here is the place all calls from utilizing GridPad are redirected to the interface implementation:

The GridPadScopeImpl is sort of a container that collects all emits from top-level DSL, builds a listing to show, and supplies the flexibility to get added composables:

Within the code above, essentially the most vital half is a metamorphosis from DSL name merchandise {} inside GridPad to meta-class GridPadContent that shops data for future measurement and placement.

Let’s return to GridPad and end the part. After we accumulate all details about placement content material the whole lot that we’d like is correct calculate sizes for every cell, measure composables, and place them into the correct place. Right here we wouldn’t deep dive into placement logic calculation, will concentrate on Jetpack Compose-related API. Step one is to measure kids based on their location and span measurement:

Many of the above code works by calculating boundaries for composables to name the measure for the positioned merchandise.

The final contact after measurement of all kids is to outline the part measurement and place objects:

Loads of work has been executed right here. The above examples may look a bit sophisticated, however Jetpack Compose mixed with Kotlin DSL supplies prospects to implement elegant APIs to your customized layouts. If you end up confronted with an issue, be happy to look into the supply code of already accomplished parts and take inspiration there, it’s the easiest way to be taught one thing new.

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles