Я до последнего верил в Navigation component от google. Но, к сожалению, ряд задач с которыми я столкнулся при его использовании вместе с compose заставили поменять мое мнение. Идея создания собственного решения для навигации меня посещала часто, но я считал, что в этом нет необходимости. Ситуация вынудила меня!!! ... и теперь я представляю - Brick.

Ну и зачем? В чем преимущество библиотеки?

Ну и сразу озвучу основную идею (и главное преимущество на мой взгляд), которую я преследовал - сделать максимально маленькое решение, которое позволит оторваться от фреймворка и строить проект как удобно. Да, отрыв от фреймворка. Например, тот же ViewModel. Штука хорошая, но проблем от нее тоже достаточно. Например, интеграция с Dagger. Ну это просто жесть, какие куски кода я видел по его интеграции. Кароче сложно, избыточно сложно.

Схема работы роутера
Схема работы роутера

В Brick же за рисование Compose отвечает отдельный компонент NavigationContainer и скормленный ему ContainerConnector. Поэтому сам Router может быть где угодно, и вызываться откуда угодно!

Ладно, и что там?

Основное понятие в Brick как я считаю - это Screen. Но это не просто Screen, который равен UI (как сразу может сложиться ассоциация). UI в Brick это часть Screen. Screen может существовать и без UI. Да, навигацию в Brick можно строить не строить от UI, а от бизнес логики. Да что это я, вы можете производить навигацию без UI! Но я не хотел этим ограничивать разработчиков. Поэтому, если вы хотите строить навигацию от UI, то это тоже можно реализовать.

Screen - имеет жизненный цикл. Не такой сложный как у Fragment, но все же он есть.
Всего два метода onCreate и onDestroy. Жизненным циклом управляет роутер.
В onCreate вы можете создать нужные зависимости и провести их в UI часть, там может быть ViewModel и прочее. Ну и собственно, Router и хранит всю эту информацию, поэтому важно, к какому ЖЦ вы привяжете сам роутер. Если это будет роутер привязанный к активити, то он будет жить пока живо активити. Если в application, то пока жив application.

Пример ЖЦ
Пример ЖЦ

Окей, не так уж и много, ради того чтобы использовать этот Brick. Еще есть что-то?

Здесь есть то, чего мне не хватало в Navigation от гугла.

1) Легче использовать, в четыре шага.

// 1 Создайте роутер
val router: TreeRouter = TreeRouter.new()
...

// 2 Создайте Screen
val screen1 = Screen<Unit>(
  key = "1",
  content = { SimpleScreen(1, "new") { smallSampleRouter.addScreen(screen2) } } // content - ui
)
...

// 3 Присоедините Container Connector (его реализует router) к NavigationContainer'у
class SmallSampleActivity : ComponentActivity() {

  val containerConnector: ContainerConnector = ... //inject or provide from application class

  override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)

      setContent {
          ScreensContainer(containerConnector)
      }
  }
}

// 4 Осуществляйте навигацию!
router.addScreen(screen1)

2) Вложенная навигация, multibackstack, multibackstack внтури multibackstack и до бесконечной фантазии вашего разработчика UX.

Пример реализации
Пример реализации

Ну, а если схематично, то выглядит это вот так:

Справедливо заметить, что она есть и в Navigation Component. Но вы ее видели?) Или хотя бы использовали?) По моему мнению, она громоздка и неудобна в использовании.

3) Child навигация. Ее очень сильно не хватало, в Navigation Component. Конечно есть навигация в диалог и есть даже навигация в Bottom Sheet (правда в accompanist). Но она рушится, когда возникает задача сделать навигацию, где два Bottom Sheet один поверх другого и еще над ними Dialog.
Да, один над другим Bottom Sheet можно сделать использовав вложенность, но как быть, если логика внутри Bottom Sheet по объемам эквивалентна целому экрану? Вмещать три логики в один ViewModel? Разбивать ViewModel? А потом теряться в этих 500+ строчках кода или схемах связки, коммуникации между ViewModel? Слишком много вопросов и слишком рискованно, выбрав неверное решение можно потом увязнуть в трудно читаемом коде.

В Brick child навигация такая же как и screen и для условного UI компонента (диалога, bottom sheet) вы можете сделать свою сущность, где будете описывать логику, только для этого UI.

4) Передача аргументов. Вы видели ее в Navigation Component? Ну окей, там у них целая идея вокруг этого, вроде как ссылка и прочее, но мне кажется это неудобным. В Brick навигацию можно осуществлять с аргументами.
5) Общение между Screen. Почему нет? Сколько раз возникает задача общения между экранами? Да постоянно! Так вот, если вам это общение между Screen необходимо, то у каждого Screen есть flow, который принимает адресованные ему данные. И отправить их можно из любого Router'a независимо от того, вложенная у вас навигация или multiback stack

6) И еще множество мелких плюшек.

Хм, интересно...

Философия этой библиотеки заложена в ее названии. Brick - кирпич. И я хочу и постараюсь сделать так, чтобы Brick стал маленьким кирпичиком в структуре Android проекта. настолько маленьким, что его интеграция не составила большого труда в любой Android проект c Jetpack Compose.

Brick на GitHub

Комментарии (2)


  1. jershell
    09.12.2021 11:17

    Выглядит неплохо, очень схож на voyager от adrielcafe. Такой вопрос, планируете реализовывать анимацию смены экранов?


    1. alphicc Автор
      09.12.2021 12:14
      +1

      Спасибо. Анимация fade (как в Navigation Component) уже есть. Остальные вида анимации также есть в планах.