![](https://habrastorage.org/getpro/habr/upload_files/a27/c54/37f/a27c5437f0cb74dd8d7f9ee2976d6796.png)
Я до последнего верил в Navigation component от google. Но, к сожалению, ряд задач с которыми я столкнулся при его использовании вместе с compose заставили поменять мое мнение. Идея создания собственного решения для навигации меня посещала часто, но я считал, что в этом нет необходимости. Ситуация вынудила меня!!! ... и теперь я представляю - Brick.
Ну и зачем? В чем преимущество библиотеки?
Ну и сразу озвучу основную идею (и главное преимущество на мой взгляд), которую я преследовал - сделать максимально маленькое решение, которое позволит оторваться от фреймворка и строить проект как удобно. Да, отрыв от фреймворка. Например, тот же ViewModel. Штука хорошая, но проблем от нее тоже достаточно. Например, интеграция с Dagger. Ну это просто жесть, какие куски кода я видел по его интеграции. Кароче сложно, избыточно сложно.
![Схема работы роутера Схема работы роутера](https://habrastorage.org/getpro/habr/upload_files/b45/ac6/3b9/b45ac63b90a1c118dbecd3689c3c18f9.png)
В 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.
![Пример ЖЦ Пример ЖЦ](https://habrastorage.org/getpro/habr/upload_files/bfd/bc3/4db/bfdbc34db459c4615c2e92669d4af0ee.png)
Окей, не так уж и много, ради того чтобы использовать этот 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.
![Пример реализации Пример реализации](https://habrastorage.org/getpro/habr/upload_files/862/f5f/0ed/862f5f0ed72448436a5e9b1310d555ed.gif)
Ну, а если схематично, то выглядит это вот так:
![](https://habrastorage.org/getpro/habr/upload_files/b02/801/bbf/b02801bbff7c2475125d65620ee645d3.png)
Справедливо заметить, что она есть и в 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