Bloco

View Original

Insetter and other updates on our Template

Last time we talked about our Android template we made a major update migrating from Java to Kotlin. This time we updated the existing libraries, added a new one, while also adding a few functions that we found useful when developing our latest project.

Libraries updates

In the past months we fixed bugs and changed some mechanics in our base libraries. The library that suffered the most changes was Dagger Hilt.

We saw the deprecation ApplicationComponent with 2.31 and the addition of TestInstallIn.

ApplicationComponent was removed as of 2.32, so if you used our template to start your Project or have issues with ApplicationComponent simply replace it with the SingletonComponent.

While UninstallModules can still be used, we recommend migrating to TestInstallIn. It allows global test replacement modules to be defined, instead of using on individual tests.

An example of TestInstallIn can bee seen below, where we replace ProductionDataServiceModule with FakeDataServiceModule.

@Module
@TestInstallIn(
    components = SingletonComponent::class,
    replaces = ProductionDataServiceModule::class)
interface FakeDataServiceModule {
  @Binds fun bind(impl: FakeDataService): DataService
}

This information and example can be explored in more detail at https://dagger.dev/hilt/testing#testinstallin

New Library

Previously we were using a helper class to handle WindowsInsets. While this class still works, it's no longer the recommended way to do it, having a few of its dependencies deprecated. However we did find an open source solution, Insetter, built by @chrisbanes.

Insetter can be used in several ways, having 2 extensions built in addition to the main library. Bellow you can find small examples and descriptions, but we recommend taking a look at the official documentation at https://chrisbanes.github.io/insetter/, where most of our information was taken from.

Main Library

The main library provides simple APIs for handling WindowInsets. To use the Library you can use its main entry point, the Builder

Insetter.builder()
    .padding(windowInsetTypesOf(navigationBars = true))`
    .margin(windowInsetTypesOf(statusBars = true))
    .applyToView(view)
or with kotlin you can also
view.applyInsetter {
    type(navigationBars = true) {
        padding()
    }

    type(navigationBars = true) {
        margin()
    }
}

Data Binding extension

Providing Data Binding-specific functionality that works with any view and allowing for a more clean code based. Using data binding, all you need is to modify your xml's where you intend to

<ImageView
    app:paddingBottomSystemWindowInsets="@{true}"
    app:paddingLeftSystemWindowInsets="@{true}" />

And to have a better Edge-to-edge you can also use the attribute app:layout_edgeToEdge that sets this view's system-ui visibility with the flags required.

Widgets extension

An extension library which provides versions of commonly used ViewGroups with enhanced inset handling. Currently this library is focusing on building upon ConstraintLayout. Currently there are two widgets you can use.

ConstraintLayout: InsetterConstraintLayout(subclass of Constraint Layout)

<dev.chrisbanes.insetter.widgets.constraintlayout.InsetterConstraintLayout
    xmlns:android="<http://schemas.android.com/apk/res/android>"
    xmlns:app="<http://schemas.android.com/apk/res-auto>" ...>

    <ImageView
	android:id="@+id/image" 
        app:paddingSystemWindowInsets="left|top|right|bottom"
     ... />

</dev.chrisbanes.insetter.widgets.constraintlayout.InsetterConstraintLayout>

or InsetterConstraintHelper which is a constraint helper.

<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="<http://schemas.android.com/apk/res/android>"
    xmlns:app="<http://schemas.android.com/apk/res-auto>" ...>

    <dev.chrisbanes.insetter.widgets.constraintlayout.InsetterConstraintHelper
        app:paddingSystemWindowInsets="left|top|right|bottom"
        app:constraint_referenced_ids="image,button" ... />

    <ImageView
        android:id="@+id/image" ... />

</androidx.constraintlayout.widget.ConstraintLayout>

While the template comes with both extensions there's no point in having both the Widgets and the Data Binding extension. So If you're already used to use data-binding we recommend using the dbx library and its binding adapters, since they work with any view type.

Result Helper Functions

To better handle potential exceptions and errors we usually wrap the Flows objects with Result. And to keep the code cleaner and consistent we introduced a few helper functions.

  • toResult

  • foldOnEach

  • mapIfSuccess

  • flatMapConcatIfSuccess

toResult wraps the flow object and guarantees that any Throwable is also wrapped in a (ResultFailure, making sure the code is free of crashes and properly handles errors.

Last but not least, foldOneEach will allow you to handle both Success and Failure each with a different function.

buttonPressed
        .asFlow()
        .flatMapConcat { domainClass.foo() }
	      .toResult()
        .foldOnEach(
          { 
              output.send(it)
          }, // Success
          { e ->
              errors.send(e)
          } // Failure
        )
        .launchIn(ioScope)
Like the name suggests ifSuccess functions will only run on (Result) Success, and propagate errors.

📲 We are a studio specialized in designing and developing Android products. Keep up with us through Twitter, Facebook, and Instagram.