Судя по отзывам коллег, https://proxyman.io - отличная альтернатива Charles Proxy.
По крайней мере, настроить моки получается проще и удобнее.
Надо бы попробовать.
А вы что используете для моков API?
Накидывайте альтернативы Charles Proxy, если юзаете что-то подобное.
По крайней мере, настроить моки получается проще и удобнее.
Надо бы попробовать.
А вы что используете для моков API?
Накидывайте альтернативы Charles Proxy, если юзаете что-то подобное.
Proxyman
Proxyman · Debug, intercept & mock HTTP with Proxyman
Proxyman is a native, high-performance macOS app, which enables developers to capture, inspect, and manipulate HTTP/HTTPS requests/responses with ease. Support iOS and Android Simulator and Physical Device.
МТС Банк поделился впечатлениями о Compose в продакшене:
https://habr.com/ru/company/ru_mts/blog/588096/
https://habr.com/ru/company/ru_mts/blog/588096/
Хабр
Использование Jetpack Compose в продакшне: первые впечатления
Мы в МТС Банке давно ждали релиза Jetpack Compose, чтобы использовать его в продакшне. В прошлом месяце такая возможность наконец появилась — мы решили обновить дизайн одного из экранов нашего...
Некоторые мысли после нескольких часов создания и запуска проекта на KMM.
Спросите: почему так много часов?
На самом деле, хотелось, чтобы проект билдился и запускался и на iOS прямиком из Android Studio. C запуском на Android как раз проблем не было.
По итогу:
1. Текущая версия KMM-плагина 0.3 работает с Kotlin 1.6 и Gradle plugin 7.0.0+. Но штука вот в чем. Если у вас не последние версии Kotlin, Java SE ниже 11-версии и студия не самая свежая (вероятно дело больше в версии Gradle Plugin), то создавая KMM-проект в AS он вылетет с ошибкой и не до конца создаст структуру проекта. И потом плагин просто disabled-тся. Обновили окружение и потом вручную включили плагин снова.
2. Для успешного запуска проекта на iOS-симуляторе нужен Xcode 12.5+ на текущей версии плагина KMM. Вероятно и версия 12.0+ сгодится, но не проверял. Но боль лично для меня в том, что Xcode 12+ доступен только на BigSur, которую я пока не ставил на рабочий iMac. Благо обновил Mac Pro и удалось немного поэкспериментировать.
3. Кстати, про Xcode. Вероятно, его можно попробовать заменить на AppCode или какими-то плагинами для Swift под AS, чтобы можно было кодить в одной среде и на Kotlin и на Swift, но скорее всего, когда проект дойдет до полноценной разработки, Xcode понадобится.
4. Дока по KMM на kotlinlang.org оставляет желать лучшего. Странное структурирование с бесконечными перекрестными ссылками, быстро устаревает, но что-то интересное почерпнуть можно и нужно. Плюс ее маловато. Хотелось бы больше инфы и кейсов.
5. В AS (есть отдельный конфиг для запуска iOS app) даже можно настроить, например, на каком симуляторе запускать проект, что однозначно радует.
6. Самое забавное - это запуск проекта на iOS. Как же забавно видеть старт iOS-симулятора прямиком из AS. И оно работает! После некоторых телодвижений :)
7. В целом, прикольные ощущения. Есть общий модуль shared с общей кодовой базой (обычно бизнес-логика), есть конкретные реализации UI на Android/iOS, которые как раз используют эту самую бизнес-логику. А если вспомнить, что Kotlin можно попробовать развернуть и на Web (Kotlin JS), то эту логику в теории можно будет использовать и там. Круто? Очень! Но без нюансов, конечно, не обойдется.
Про структуру проекта поговорим подробнее чуть позже.
Итак, рабочая конфигурация для плагина KMM 0.3:
- Java SE 11 (можно попробовать и OpenJDK)
- AS Arctic Fox 2020.3.1 Patch 3
- Xcode 12.5.1
- Kotlin 1.6.0
- Gradle Plugin 7.0.3
Спросите: почему так много часов?
На самом деле, хотелось, чтобы проект билдился и запускался и на iOS прямиком из Android Studio. C запуском на Android как раз проблем не было.
По итогу:
1. Текущая версия KMM-плагина 0.3 работает с Kotlin 1.6 и Gradle plugin 7.0.0+. Но штука вот в чем. Если у вас не последние версии Kotlin, Java SE ниже 11-версии и студия не самая свежая (вероятно дело больше в версии Gradle Plugin), то создавая KMM-проект в AS он вылетет с ошибкой и не до конца создаст структуру проекта. И потом плагин просто disabled-тся. Обновили окружение и потом вручную включили плагин снова.
2. Для успешного запуска проекта на iOS-симуляторе нужен Xcode 12.5+ на текущей версии плагина KMM. Вероятно и версия 12.0+ сгодится, но не проверял. Но боль лично для меня в том, что Xcode 12+ доступен только на BigSur, которую я пока не ставил на рабочий iMac. Благо обновил Mac Pro и удалось немного поэкспериментировать.
3. Кстати, про Xcode. Вероятно, его можно попробовать заменить на AppCode или какими-то плагинами для Swift под AS, чтобы можно было кодить в одной среде и на Kotlin и на Swift, но скорее всего, когда проект дойдет до полноценной разработки, Xcode понадобится.
4. Дока по KMM на kotlinlang.org оставляет желать лучшего. Странное структурирование с бесконечными перекрестными ссылками, быстро устаревает, но что-то интересное почерпнуть можно и нужно. Плюс ее маловато. Хотелось бы больше инфы и кейсов.
5. В AS (есть отдельный конфиг для запуска iOS app) даже можно настроить, например, на каком симуляторе запускать проект, что однозначно радует.
6. Самое забавное - это запуск проекта на iOS. Как же забавно видеть старт iOS-симулятора прямиком из AS. И оно работает! После некоторых телодвижений :)
7. В целом, прикольные ощущения. Есть общий модуль shared с общей кодовой базой (обычно бизнес-логика), есть конкретные реализации UI на Android/iOS, которые как раз используют эту самую бизнес-логику. А если вспомнить, что Kotlin можно попробовать развернуть и на Web (Kotlin JS), то эту логику в теории можно будет использовать и там. Круто? Очень! Но без нюансов, конечно, не обойдется.
Про структуру проекта поговорим подробнее чуть позже.
Итак, рабочая конфигурация для плагина KMM 0.3:
- Java SE 11 (можно попробовать и OpenJDK)
- AS Arctic Fox 2020.3.1 Patch 3
- Xcode 12.5.1
- Kotlin 1.6.0
- Gradle Plugin 7.0.3
И сразу небольшой анонс на ближайшие (и не очень) планы.
KMM понравился, поэтому будем пошагово ковырять. Есть ощущение, что за этим будущее, но поглядим.
Создал репу: https://github.com/Djangist/ToDoApp в которой будем делать кроссплатформенный Todo App на KMM. Уже можно стянуть пустую болванку, настроить окружение, попробовать собрать и запустить.
Что хотелось бы сделать в этом аппе?
1. Общую бизнес-логику заметок для обеих платформ.
2. Общий Rest API c репозиториями, интеракторами и вот этим всем. Например, прикрутить что-то вроде: https://developers.google.com/tasks/reference/rest для синхронизации задач из Google-акка. Но надо подумать.
3. Compose / SwiftUI, а может к этому времени сделают поддержку Compose и на iOS? Есть уже Compose Multiplatform.
4. MVI-архитектура для presеntation-слоя.
5. Базовый функционал: CRUD-интерфейс, sharing заметок, оффлайн-режим.
6. Кроссплатформенный DI? Уфф...
7. Предлагайте свои идеи!
Flutter тоже будем ковырять. Он не менее интересен, чем KMM. По крайней мере, для ниши «создать быстрый кроссплатформенный прототип» под все платформы - самое то!
Как раз, есть мысль переписать свой старенький апп, но пока еще не решил на какую платформу.
Будем дальше разбираться с Compose и MVI.
Доделаю собственный велосипед для MVI. Пока там есть проблемы, но не хватает времени.
И само собой, будем и дальше вникать в тонкости самого Android, Kotlin, и немножко изучать Swift / iOS.
KMM понравился, поэтому будем пошагово ковырять. Есть ощущение, что за этим будущее, но поглядим.
Создал репу: https://github.com/Djangist/ToDoApp в которой будем делать кроссплатформенный Todo App на KMM. Уже можно стянуть пустую болванку, настроить окружение, попробовать собрать и запустить.
Что хотелось бы сделать в этом аппе?
1. Общую бизнес-логику заметок для обеих платформ.
2. Общий Rest API c репозиториями, интеракторами и вот этим всем. Например, прикрутить что-то вроде: https://developers.google.com/tasks/reference/rest для синхронизации задач из Google-акка. Но надо подумать.
3. Compose / SwiftUI, а может к этому времени сделают поддержку Compose и на iOS? Есть уже Compose Multiplatform.
4. MVI-архитектура для presеntation-слоя.
5. Базовый функционал: CRUD-интерфейс, sharing заметок, оффлайн-режим.
6. Кроссплатформенный DI? Уфф...
7. Предлагайте свои идеи!
Flutter тоже будем ковырять. Он не менее интересен, чем KMM. По крайней мере, для ниши «создать быстрый кроссплатформенный прототип» под все платформы - самое то!
Как раз, есть мысль переписать свой старенький апп, но пока еще не решил на какую платформу.
Будем дальше разбираться с Compose и MVI.
Доделаю собственный велосипед для MVI. Пока там есть проблемы, но не хватает времени.
И само собой, будем и дальше вникать в тонкости самого Android, Kotlin, и немножко изучать Swift / iOS.
GitHub
GitHub - Djangist/ToDoApp
Contribute to Djangist/ToDoApp development by creating an account on GitHub.
Старый Мобильщик pinned «И сразу небольшой анонс на ближайшие (и не очень) планы. KMM понравился, поэтому будем пошагово ковырять. Есть ощущение, что за этим будущее, но поглядим. Создал репу: https://github.com/Djangist/ToDoApp в которой будем делать кроссплатформенный Todo App…»
Уже совсем скоро стартует бесплатная онлайн-конфа от Яндекса: https://yatalks.yandex.ru/
Надеюсь будет интересно!
Надеюсь будет интересно!
yatalks.yandex.ru
YaTalks 2023 — Yandex's premier conference for the IT community
On December 5-6, Moscow and Belgrade will host over 100 IT industry experts and scientists delivering technical presentations on development, ML, and giving popular science lectures.
Ого!
Только мы недавно вспоминали Compose Multiplatform, как JetBrains анонсировали выход версии 1.0:
https://blog.jetbrains.com/kotlin/2021/12/compose-multiplatform-1-0-is-going-live/
Только мы недавно вспоминали Compose Multiplatform, как JetBrains анонсировали выход версии 1.0:
https://blog.jetbrains.com/kotlin/2021/12/compose-multiplatform-1-0-is-going-live/
The JetBrains Blog
Compose Multiplatform 1.0 Is Going Live! | The Kotlin Blog
Compose Multiplatform by JetBrains, the declarative UI framework for Kotlin, has reached version 1.0, which makes it ready for production use! Here are a few highlights that we hope will make you as e
Принцип проектирования по контракту
Бертран Мейер разработал принцип проектирования по контракту для языка Eiffel. Это простая, но эффективная методика, направленная на документирование (и согласование) прав и обязанностей программных модулей, чтобы обеспечить правильность программы. А что такое правильная программа? Это программа, которая выполняет только то, что от нее требуется, - ни больше и ни меньше. Документирование и верификация такого требования и составляет саму суть проектирования по контракту.
Каждая функция и метод в программной системе выполняет какое-то действие. Прежде чем начать это действие, функция может предполагать какое-то состояние окружающего мира, а по завершении она может сделать заявление о состоянии окружающего мира (перевод на русский, вероятно, не самый удачный, но суть отражает). Эти предположения и требования Мейер описывает следующим образом.
- Предусловия. Это требования подпрограммы, которые определяют, что должно быть истинным для ее вызова. Подпрограмма вообще не должна вызываться, если ее предусловия будут нарушены.
- Постусловия. Это состояние окружающего мира по завершении подпрограммы, т.е. То, что ею гарантируется. Наличие постусловия у подпрограммы подразумевает, что она непременно завершится, а следовательно, бесконечные циклы не допускаются.
- Инварианты класса. Класс гарантирует, что данное условие для вызывающего кода всегда истинно. В процессе внутренней обработки в подпрограмме инвариант может и не соблюдаться, но к моменту выхода из подпрограммы и передачи управления вызывающему коду инвариант должен быть непременно истинным.
Таким образом, контракт между подпрограммой и любым потенциально вызывающим кодом может быть составлен следующим образом.
- Если все предусловия подпрограммы удовлетворяются вызывающим кодом, то подпрограмма гарантирует истинность всех постусловий и инвариантов при своем завершении.
К чему это все?
Оказывается, в Kotlin 1.3 появился пакет kotlin.contracts и у нас есть возможность попробовать использовать этот принцип на практике, делая наши приложения более документированными и безопасными.
А вот примеры использования этого пакета: https://blog.kotlin-academy.com/understanding-kotlin-contracts-f255ded41ef2
Кстати, а вы слышали про подобный принцип проектирования ранее?
Бертран Мейер разработал принцип проектирования по контракту для языка Eiffel. Это простая, но эффективная методика, направленная на документирование (и согласование) прав и обязанностей программных модулей, чтобы обеспечить правильность программы. А что такое правильная программа? Это программа, которая выполняет только то, что от нее требуется, - ни больше и ни меньше. Документирование и верификация такого требования и составляет саму суть проектирования по контракту.
Каждая функция и метод в программной системе выполняет какое-то действие. Прежде чем начать это действие, функция может предполагать какое-то состояние окружающего мира, а по завершении она может сделать заявление о состоянии окружающего мира (перевод на русский, вероятно, не самый удачный, но суть отражает). Эти предположения и требования Мейер описывает следующим образом.
- Предусловия. Это требования подпрограммы, которые определяют, что должно быть истинным для ее вызова. Подпрограмма вообще не должна вызываться, если ее предусловия будут нарушены.
- Постусловия. Это состояние окружающего мира по завершении подпрограммы, т.е. То, что ею гарантируется. Наличие постусловия у подпрограммы подразумевает, что она непременно завершится, а следовательно, бесконечные циклы не допускаются.
- Инварианты класса. Класс гарантирует, что данное условие для вызывающего кода всегда истинно. В процессе внутренней обработки в подпрограмме инвариант может и не соблюдаться, но к моменту выхода из подпрограммы и передачи управления вызывающему коду инвариант должен быть непременно истинным.
Таким образом, контракт между подпрограммой и любым потенциально вызывающим кодом может быть составлен следующим образом.
- Если все предусловия подпрограммы удовлетворяются вызывающим кодом, то подпрограмма гарантирует истинность всех постусловий и инвариантов при своем завершении.
К чему это все?
Оказывается, в Kotlin 1.3 появился пакет kotlin.contracts и у нас есть возможность попробовать использовать этот принцип на практике, делая наши приложения более документированными и безопасными.
А вот примеры использования этого пакета: https://blog.kotlin-academy.com/understanding-kotlin-contracts-f255ded41ef2
Кстати, а вы слышали про подобный принцип проектирования ранее?
Medium
Understanding Kotlin contracts
Since Kotlin 1.3 we can enjoy new, mysterious feature — Kotlin contracts. From the usage perspective, they look just like a piece of code…
Небольшой дайджест изменений во Fragment API 1.4.0 (релиз от 17 Ноября)
- Появился метод FragmentContainerView.getFragment()
Раньше, чтобы получить фрагмент нужно было дергать метод:
- Поддержка множественных back stacks (также доступно в Navigation Component)
Подробнее здесь: https://medium.com/androiddevelopers/multiple-back-stacks-b714d974f134
- FragmentStrictMode - разные проверки потенциальных проблем при работе с фрагментами и возможность на них реагировать.
К примеру, можно отправить какие-то кастомные исключения/события в Firebase Crashlytics.
Задать настройки можно через метод setStrictModePolicy()
Подробнее:https://developer.android.com/guide/fragments/debugging#strictmode
- Новые проверки для Android Lint
- FragmentScenario теперь реализует Closeable и их можно использовать в Kotlin-овском use и try-with-resources.
Подробнее про FragmentScenario и тестирование фрагментов: https://developer.android.com/guide/fragments/test
- Появился метод FragmentContainerView.getFragment()
Раньше, чтобы получить фрагмент нужно было дергать метод:
val fragment = fragmentManager.findFragmentById()
и приводить к нужному типу, а теперь же фрагмент можно получить через метод контейнера: val fragment = binding.container.getFragment<NavHostFragment>()
- Новый менеджер состояний (до 1.4.0 был экспериментальным)- Поддержка множественных back stacks (также доступно в Navigation Component)
Подробнее здесь: https://medium.com/androiddevelopers/multiple-back-stacks-b714d974f134
- FragmentStrictMode - разные проверки потенциальных проблем при работе с фрагментами и возможность на них реагировать.
К примеру, можно отправить какие-то кастомные исключения/события в Firebase Crashlytics.
Задать настройки можно через метод setStrictModePolicy()
Подробнее:https://developer.android.com/guide/fragments/debugging#strictmode
- Новые проверки для Android Lint
- FragmentScenario теперь реализует Closeable и их можно использовать в Kotlin-овском use и try-with-resources.
Подробнее про FragmentScenario и тестирование фрагментов: https://developer.android.com/guide/fragments/test
Medium
Multiple back stacks
A deep dive into what actually went into bring this feature to Fragments and Jetpack Navigation
Друзья, а вы пользуетесь итераторами?
Просто пост-напоминание, что иногда и они могут быть полезны.
Простая задачка: поменять значение одного из элементов списка, индекс которого мы не знаем.
Можно воспользоваться listIterator() таким вот образом:
Но изменять значения итератор само собой позволит только у MutableList. У обычного List метода set нет, что логично.
Просто пост-напоминание, что иногда и они могут быть полезны.
Простая задачка: поменять значение одного из элементов списка, индекс которого мы не знаем.
Можно воспользоваться listIterator() таким вот образом:
val someList = mutableListOf(1, 20, 10, 55, 30, 22, 11, 0, 99) // или arrayListOf()
val iterate = someList.listIterator()
while (iterate.hasNext()) {
val oldValue = iterate.next()
if (oldValue == 20) {
iterate.set(oldValue + 20)
}
}
Но изменять значения итератор само собой позволит только у MutableList. У обычного List метода set нет, что логично.
Давайте поиграем в игру "Собеседование".
Представьте, что собеседуете человека.
На скрине код кандидата.
1. Что в нем не так? Сколько проблем сходу видите?
2. На какой уровень должен претендовать кандидат с таким кодом? (jun / middle / senior / lead )
3. Вопросы по каким темам вы бы у него спросили после ознакомления с таким кодом?
Представьте, что собеседуете человека.
На скрине код кандидата.
1. Что в нем не так? Сколько проблем сходу видите?
2. На какой уровень должен претендовать кандидат с таким кодом? (jun / middle / senior / lead )
3. Вопросы по каким темам вы бы у него спросили после ознакомления с таким кодом?
И так, продолжим с ответов на вопросы выше про код кандидата. Придется отвечать мне, раз уж вы такие молчаливые 🙂
Кандидат хотел устроиться к нам в банк, в довольно сложный стрим. Особо про его резюме не будем. Парень окончил курсы на Udemy, поработав годик-около двух.
Хотелось бы послушать про его опыт, а то там уже и джунов полидить успел, при том что в коде, который он прислал нам, активно участвовал еще один разраб. Однако, кандидат принял оффер другой кампании и к нам на собес не пришел.
Но давайте все таки про код.
1. Напрямую в репозиторий передается экземпляр Application. Во-первых, это мягко говоря не очень из-за возможных проблем с утечками, а во-вторых в репозитории не нужно передевать никакие зависимости от Android.
2. Так называемый mapper (CoinMapper) не передается в качестве зависимости в конструкторе и сам маппер написано неправильно.
3. И вообще, зависимость подставляется вручную, не через DI-контейнер. Для примера не прям уж критично, если человек может рассказать почему так и как работает DI, но даже использовав условный Hilt можно добавить себе "дополнительных очков" в коде.
4. Экземпляр БД тоже лучше передавать в зависимость в конструкторе.
5. Репозиторий не должен возвращать LiveData (опять же, пункт про Application). Репозиторий должен возвращать список моделей, а дальше уже на уровне Presentation решается как с этим быть дальше.
6. Небрежное форматирование. Писали быстро. Не критично, но такая небрежность настораживает.
7. Работу с WorkManager можно смело вынести в отдельную зависимость.
Кандидата стоит смело погонять по Clean Architecture, MVVM, MVP, leak memory, многомодульности (у нас очень много модулей), DI и само собой по основам Android.
Исходя из проблем выше, кандидата можно оценить как Джуниор-разработчика, может быть Джуниор+/начинающий Миддл если бы мы послушали про его опыт. Здесь важно сопоставить архитектуру и проблемы вашего проекта и опыт кандидата. Но судя по резюме, человек себя позиционирует выше Джуна, что немножко настораживает после увиденного кода.
Таких кандидатов бояться не стоит, но на сложный стрим если и брать, то в пару с ментором, который будет его вести, что в реальной жизни крайне редко встречается.
Кандидат хотел устроиться к нам в банк, в довольно сложный стрим. Особо про его резюме не будем. Парень окончил курсы на Udemy, поработав годик-около двух.
Хотелось бы послушать про его опыт, а то там уже и джунов полидить успел, при том что в коде, который он прислал нам, активно участвовал еще один разраб. Однако, кандидат принял оффер другой кампании и к нам на собес не пришел.
Но давайте все таки про код.
1. Напрямую в репозиторий передается экземпляр Application. Во-первых, это мягко говоря не очень из-за возможных проблем с утечками, а во-вторых в репозитории не нужно передевать никакие зависимости от Android.
2. Так называемый mapper (CoinMapper) не передается в качестве зависимости в конструкторе и сам маппер написано неправильно.
3. И вообще, зависимость подставляется вручную, не через DI-контейнер. Для примера не прям уж критично, если человек может рассказать почему так и как работает DI, но даже использовав условный Hilt можно добавить себе "дополнительных очков" в коде.
4. Экземпляр БД тоже лучше передавать в зависимость в конструкторе.
5. Репозиторий не должен возвращать LiveData (опять же, пункт про Application). Репозиторий должен возвращать список моделей, а дальше уже на уровне Presentation решается как с этим быть дальше.
6. Небрежное форматирование. Писали быстро. Не критично, но такая небрежность настораживает.
7. Работу с WorkManager можно смело вынести в отдельную зависимость.
Кандидата стоит смело погонять по Clean Architecture, MVVM, MVP, leak memory, многомодульности (у нас очень много модулей), DI и само собой по основам Android.
Исходя из проблем выше, кандидата можно оценить как Джуниор-разработчика, может быть Джуниор+/начинающий Миддл если бы мы послушали про его опыт. Здесь важно сопоставить архитектуру и проблемы вашего проекта и опыт кандидата. Но судя по резюме, человек себя позиционирует выше Джуна, что немножко настораживает после увиденного кода.
Таких кандидатов бояться не стоит, но на сложный стрим если и брать, то в пару с ментором, который будет его вести, что в реальной жизни крайне редко встречается.
👍1
Наконец-то стало больше времени и теперь поговорим о KMM-проекте.
Напомню локацию: https://github.com/Djangist/ToDoApp
Что нового:
- Compose теперь работает и на Kotlin 1.6 (версия Compose Compiler должна быть 1.1.0-rc02)
- Появился набросок главного экрана со списком заметок. На обеих платформах. Для iOS используется SwiftUI, который появился, к слову, раньше чем Compose и очень похож по концепции. Самая сложная часть была именно здесь (ну, понятно почему). А вот на Compose главный экран накидать удается довольно быстро.
- Общий модуль shared корректно подключается в оба проектах. В этом модуле у нас будет вся бизнес-логика. Сейчас это модель Todo и репа TodoRepository. В дальнейшем здесь будет все по Clean: интеракторы и прочие общие вещи.
- Типы в Kotlin немного отличаются от типов в Swift. Пример наш репозиторий, который возвращает List<Todo>. В Swift это будет Array<Todo>. Благо в доке есть сравнительная табличка.
- Если вы решите писать оба проекта сами (а во многом на это и будет расчет у заказчиков) - учите iOS / Swift / SwiftUI. Пришлось потратить около часа! чтобы разобраться как динамически вывести список Todo. В этом нам помогла функция.
И в этом отношении Flutter выглядит для простых проектов интереснее: один язык для всего, а выучить Dart зная условную Java не так сложно!
- XCode - это боль! Ну либо я к нему все еще не привык. Автодополнение работает через жопу, все медленно, не интуитивно. Редактировать код просто не комфортно. Но хочется похвалить работу Preview - оно проще и быстрее чем в AS для Compose. В теории, можно обойтись и без XCode, но там больше нужных вещей и конфиги редактировать иногда придется. Все еще мечтаю попробовать в январе AppCode.
- MacPro 16 c интелом на борту подлагивает при открытых проектах и эмуляторе. Работать можно, но уже не так комфортно как в одной лишь AS.
- Нужно привыкать к яблочной доке. Если вы ругаете доку от Гугла, посмотрите яблочную ) Мало того, что найти нужную инфу не так просто, так еще и инфы как-то не хватает и структурировано все не очень явно. В стиле яблока короче.
- Ключевая (ну или одна) из концепций в KMM - это expect / actual. Когда в общем модуле shared нужен какой-то платформенно-специфичный код вы объявляете class / fun как expect, а в конкретном пакете того же модуля потом объявленный класс или функцию реализуете (actual class / fun). Пример на скрине.
- Иконки. В Compose вы просто используете Icon(Icons.Filled.Add) выбирая нужную в AS, но в iOS все конечно же не так... Оказывается там системную иконку нужно выбрать по названию строки: Image(systemName: "plus.circle.fill")
Какой пипец. Но не спешите расстраиваться. Есть отдельное приложение для просмотра / поиска системных иконок - https://developer.apple.com/design/human-interface-guidelines/sf-symbols/overview/ Непривычно, что по названию строки нужно выбирается иконка, но хрен с ним - можно привыкнуть.
На этом из нового пока все.
С наступающим вас!
Пусть в 2022-ом в вашем коде будет как можно меньше багов и как можно больше оплаченных строк и интересных проектов!
Напомню локацию: https://github.com/Djangist/ToDoApp
Что нового:
- Compose теперь работает и на Kotlin 1.6 (версия Compose Compiler должна быть 1.1.0-rc02)
- Появился набросок главного экрана со списком заметок. На обеих платформах. Для iOS используется SwiftUI, который появился, к слову, раньше чем Compose и очень похож по концепции. Самая сложная часть была именно здесь (ну, понятно почему). А вот на Compose главный экран накидать удается довольно быстро.
- Общий модуль shared корректно подключается в оба проектах. В этом модуле у нас будет вся бизнес-логика. Сейчас это модель Todo и репа TodoRepository. В дальнейшем здесь будет все по Clean: интеракторы и прочие общие вещи.
- Типы в Kotlin немного отличаются от типов в Swift. Пример наш репозиторий, который возвращает List<Todo>. В Swift это будет Array<Todo>. Благо в доке есть сравнительная табличка.
- Если вы решите писать оба проекта сами (а во многом на это и будет расчет у заказчиков) - учите iOS / Swift / SwiftUI. Пришлось потратить около часа! чтобы разобраться как динамически вывести список Todo. В этом нам помогла функция.
let todos = TodoRepositoryImpl().getAllTodos() // получаем список заметок (код в модуле shared)
...
VStack(spacing: 8) {
ForEach(todos, id: \.self){ todo in
VStack {
Text(todo.description_)
.font(.headline)
.padding(.top)
.padding(.leading)
.frame(maxWidth: .infinity,
alignment: .leading)
Text(todo.text)
.font(.subheadline)
.padding(.leading)
.frame(maxWidth: .infinity,
alignment: .leading)
}
}
Spacer()
И в этом отношении Flutter выглядит для простых проектов интереснее: один язык для всего, а выучить Dart зная условную Java не так сложно!
- XCode - это боль! Ну либо я к нему все еще не привык. Автодополнение работает через жопу, все медленно, не интуитивно. Редактировать код просто не комфортно. Но хочется похвалить работу Preview - оно проще и быстрее чем в AS для Compose. В теории, можно обойтись и без XCode, но там больше нужных вещей и конфиги редактировать иногда придется. Все еще мечтаю попробовать в январе AppCode.
- MacPro 16 c интелом на борту подлагивает при открытых проектах и эмуляторе. Работать можно, но уже не так комфортно как в одной лишь AS.
- Нужно привыкать к яблочной доке. Если вы ругаете доку от Гугла, посмотрите яблочную ) Мало того, что найти нужную инфу не так просто, так еще и инфы как-то не хватает и структурировано все не очень явно. В стиле яблока короче.
- Ключевая (ну или одна) из концепций в KMM - это expect / actual. Когда в общем модуле shared нужен какой-то платформенно-специфичный код вы объявляете class / fun как expect, а в конкретном пакете того же модуля потом объявленный класс или функцию реализуете (actual class / fun). Пример на скрине.
- Иконки. В Compose вы просто используете Icon(Icons.Filled.Add) выбирая нужную в AS, но в iOS все конечно же не так... Оказывается там системную иконку нужно выбрать по названию строки: Image(systemName: "plus.circle.fill")
Какой пипец. Но не спешите расстраиваться. Есть отдельное приложение для просмотра / поиска системных иконок - https://developer.apple.com/design/human-interface-guidelines/sf-symbols/overview/ Непривычно, что по названию строки нужно выбирается иконка, но хрен с ним - можно привыкнуть.
На этом из нового пока все.
С наступающим вас!
Пусть в 2022-ом в вашем коде будет как можно меньше багов и как можно больше оплаченных строк и интересных проектов!
Наткнулся на довольно неплохую подборку либ для KMM:
https://github.com/AAkira/Kotlin-Multiplatform-Libraries
Радует, что их все больше и больше!
https://github.com/AAkira/Kotlin-Multiplatform-Libraries
Радует, что их все больше и больше!
GitHub
GitHub - AAkira/Kotlin-Multiplatform-Libraries: Kotlin Multiplatform Libraries. Welcome PR if you find or create new Kotlin Multiplatform…
Kotlin Multiplatform Libraries. Welcome PR if you find or create new Kotlin Multiplatform Library. - AAkira/Kotlin-Multiplatform-Libraries
👍1
Попробовал таки AppCode для iOS-проекта (который часть KMM-проекта).
Что понравилось?
- Автодополнение и подсветка параметров функций и методов работает лучше, чем в Xcode. В целом с кодом приятно работать (как и в любой IDEA-based среде)
- Проект без проблем запускается на iOS-симуляторе
- Все доступные симуляторы среда также подсвечивает (как обычно сделано в Android Studio)
- Проект без проблем импортируется и открывается.
- Можно редактировать и .plist-файлы и pod-файлы.
Чего явно не хватает?
- Превью для SwiftUI. Все таки фишка приятная и не хочется от нее отказываться.
- Редактирование конфига проекта. Для новичка в iOS это все таки удобно и тут Xcode снова выигрывает.
В целом впечатления от AppCode приятные и видится, что JetBrains скоро допилит и эти фичи тоже, но пока среда не выглядит, как полноценная замена XCode.
Отладку пока толком не смотрел, но из того, что видил в Xcode она пока еще более мощная (там и память и сеть и дебаггинг более плотный, но вот тут надо сравнивать).
Опыта в AppCode (да и в iOS), конечно, маловато и я еще повожусь с ней, пока действует Trial до конца Января, но поймал себя на мысли, что все таки привычка работать в бесплатной, классной среде сказывается (это я про Android Studio), поэтому для себя пока не понял стоит ли платить 200$ в год за AppCode .
Поддержать JetBrains?
Это, конечно да, но я сейчас о скорее о полноценной замене XCode, а то как-то много сред приходится держать открытыми!
Что понравилось?
- Автодополнение и подсветка параметров функций и методов работает лучше, чем в Xcode. В целом с кодом приятно работать (как и в любой IDEA-based среде)
- Проект без проблем запускается на iOS-симуляторе
- Все доступные симуляторы среда также подсвечивает (как обычно сделано в Android Studio)
- Проект без проблем импортируется и открывается.
- Можно редактировать и .plist-файлы и pod-файлы.
Чего явно не хватает?
- Превью для SwiftUI. Все таки фишка приятная и не хочется от нее отказываться.
- Редактирование конфига проекта. Для новичка в iOS это все таки удобно и тут Xcode снова выигрывает.
В целом впечатления от AppCode приятные и видится, что JetBrains скоро допилит и эти фичи тоже, но пока среда не выглядит, как полноценная замена XCode.
Отладку пока толком не смотрел, но из того, что видил в Xcode она пока еще более мощная (там и память и сеть и дебаггинг более плотный, но вот тут надо сравнивать).
Опыта в AppCode (да и в iOS), конечно, маловато и я еще повожусь с ней, пока действует Trial до конца Января, но поймал себя на мысли, что все таки привычка работать в бесплатной, классной среде сказывается (это я про Android Studio), поэтому для себя пока не понял стоит ли платить 200$ в год за AppCode .
Поддержать JetBrains?
Это, конечно да, но я сейчас о скорее о полноценной замене XCode, а то как-то много сред приходится держать открытыми!
👍1
Поигрался немного со Swift попутно просматривая курс для новичков:
https://www.youtube.com/watch?v=ZJcVUSfdVyo
Не смотря на то, что курс больше для новичков в программировании, нашел там для себя ответы на вопросы по синтаксису языка и остался доволен потраченными 5 часами.
Также закинул на Gist свой .playground, который набирал во время просмотра видео (кому-то может сэкономит время):
https://gist.github.com/Djangist/5d7e1cedf01ced57b0f399624fafd1fa
Playground - специальный режим XCode, позволяющий поиграться с языком, при чем довольно удобный: можно писать какие-то куски кода, быстро прогнать или например удобно решать какие-то алгоритмические задачки, пробовать фичи языка.
Как это выглядит показано на скриншоте.
Если кто надумает поучить Swift - рекомендую начать с этого курса + оф дока на сайте swift.org
Если будут нужны еще какие-то материалы по Swift - пишите в комменты.
https://www.youtube.com/watch?v=ZJcVUSfdVyo
Не смотря на то, что курс больше для новичков в программировании, нашел там для себя ответы на вопросы по синтаксису языка и остался доволен потраченными 5 часами.
Также закинул на Gist свой .playground, который набирал во время просмотра видео (кому-то может сэкономит время):
https://gist.github.com/Djangist/5d7e1cedf01ced57b0f399624fafd1fa
Playground - специальный режим XCode, позволяющий поиграться с языком, при чем довольно удобный: можно писать какие-то куски кода, быстро прогнать или например удобно решать какие-то алгоритмические задачки, пробовать фичи языка.
Как это выглядит показано на скриншоте.
Если кто надумает поучить Swift - рекомендую начать с этого курса + оф дока на сайте swift.org
Если будут нужны еще какие-то материалы по Swift - пишите в комменты.
Виджет поиска на Compose. Часть 1. Базовая реализация.
Мы в команде постепенно создаем компонентную базу на Compose, чтобы в недалеком будущем перейти полностью на новый UI. Переход будет плавным, поэтому будет время набить нужные шишки с Compose.
Взял себе задачку создать компонент поиска - как раз будет что рассказать вам!
В этой часте поговорим про базовую реализацию и начнем сразу с кода.
- Самое сложное на первом этапе - найти все нужные атрибуты базового виджета (проще всего через исходники) и спроектировать параметры нашего компонента.
- Все что нужно настроить извне выносим в параметры функции (например, цвета, размер шрифта и callback, который дергается во время ввода)
- Почти все параметры со значением по умолчанию для гибкости и простоты вызова.
- Placeholder может быть довольно сложным благодаря Composable-лямбде.
- Хорошая практика - передавать в кастомный компонент modifier, который настраивается извне. В этой часте пока это не реализовано, но будет добавлено чуть позже.
Пока ничего сложного, однако стоит еще добавить работу с состоянием компонента (кеш) и другие мелочи, чем позже и займемся.
Скрин как это выглядит в Preview.
Мы в команде постепенно создаем компонентную базу на Compose, чтобы в недалеком будущем перейти полностью на новый UI. Переход будет плавным, поэтому будет время набить нужные шишки с Compose.
Взял себе задачку создать компонент поиска - как раз будет что рассказать вам!
В этой часте поговорим про базовую реализацию и начнем сразу с кода.
@ExperimentalUnitApi
@Composable
fun SearchView(
value: String = "",
hintText: String = "",
hintColor: Color = Color(0xFF797F90),
contentDescription: String = "search widget",
cornerRadius: Dp = 12.dp,
backgroundColor: Color = Color(0xFFF5F5F7),
textChangedCallback: (String) -> Unit = {}
) {
TextField(
modifier = Modifier.background(
color = backgroundColor,
),
value = value,
leadingIcon = {
Icon(
modifier = Modifier.size(24.dp),
painter = painterResource(id = R.drawable.ic_search),
contentDescription = contentDescription,
)
},
singleLine = true,
onValueChange = { textChangedCallback(it) },
shape = RoundedCornerShape(cornerRadius),
colors = TextFieldDefaults.textFieldColors(
placeholderColor = hintColor,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent
),
placeholder = {
Text(
text = hintText,
style = TextStyle(fontSize = TextUnit(16.0f, TextUnitType.Sp))
)
}
)
}
- По большому счету весь компонент строится вокруг поля ввода - TextField. Посмотрим позже достаточно ли этого или придется вложить его в Row.- Самое сложное на первом этапе - найти все нужные атрибуты базового виджета (проще всего через исходники) и спроектировать параметры нашего компонента.
- Все что нужно настроить извне выносим в параметры функции (например, цвета, размер шрифта и callback, который дергается во время ввода)
- Почти все параметры со значением по умолчанию для гибкости и простоты вызова.
- Placeholder может быть довольно сложным благодаря Composable-лямбде.
- Хорошая практика - передавать в кастомный компонент modifier, который настраивается извне. В этой часте пока это не реализовано, но будет добавлено чуть позже.
Пока ничего сложного, однако стоит еще добавить работу с состоянием компонента (кеш) и другие мелочи, чем позже и займемся.
Скрин как это выглядит в Preview.
👍2
DI в KMM. Первые пробы с Koin.
Koin был одним из первых DI-фреймворков, кто начал поддерживать KMM. Но стоит напомнить, что Koin - Service Locator, поэтому DI с ним довольно условный. В простом проекте иногда можно (будем себя так успокаивать) для быстрого старта. Запасайтесь кофем - букв много.
Чтобы добавить поддержку Koin в наш KMM-проект нужно:
1. Добавляем все зависимости Koin в модуль shared
2. Добавляем в пакет shared/commonMain код в файл Koin.kt:
commonModule - наш общий Koin-модуль с зависимостями для обеих платформ.
initKoin соответственно мы будем дергать из Android-приложения, а initKoinIos - ну вы поняли.
3. в iosMain мы должны добавить свою реализацию метода получения зависимостей, так как на входе у нас будет другой тип. Создаем в этом пакете Koin.kt с кодом:
Теперь вызываем на каждый платформе нужную версию метода init и в целом из основного все.
В Android мы делаем дальше все как обычно, вызывая Koin-ский inject() и добавляя либы Koin-а к проекту. Получаем наш общий репозиторий как обычно:
Вы можете заметить, что мы получаем Impl, а не интерфейс и будете правы. Пока что с типами есть проблема. Если указывать в модуле именно интерфейс - краш на iOS, если поменять тип при инжекте в Android - краш на Android. Так что тут есть проблемка над которой стоило бы посидеть.
В iOS мы используем Koin немного иначе.
Создаем Koin.swift с небольшим хелпером:
startKoin мы дергаем в App (как впрочем и на Android)
Обратите внимание, что метод initKoiniOS на iOS стал называться doInitKoinIOS(). Также мы добавляем экстеншен для типа Koin_coreKoin для небольшого сахарка. С помощью этого метода и будем получать нужные зависимости в ContentView:
С Koin-ом пока наверное закончим.
Примерно так чаще всего реализуется DI в KMM.
Дальше есть мысль попробовать настоящий DI: https://github.com/sergeshustoff/dikt
Хочется получать не краши, а ошибки компиляции в случае проблем с зависимостями. В KMM-проектах это будет экономить кучу времени при отладке!
Koin был одним из первых DI-фреймворков, кто начал поддерживать KMM. Но стоит напомнить, что Koin - Service Locator, поэтому DI с ним довольно условный. В простом проекте иногда можно (будем себя так успокаивать) для быстрого старта. Запасайтесь кофем - букв много.
Чтобы добавить поддержку Koin в наш KMM-проект нужно:
1. Добавляем все зависимости Koin в модуль shared
2. Добавляем в пакет shared/commonMain код в файл Koin.kt:
fun initKoin() = startKoin {
printLogger(Level.ERROR)
modules(commonModule)
}
// called by iOS etc
fun initKoinIos() = startKoin {
printLogger(Level.ERROR)
modules(commonModule)
}
val commonModule = module {
single { TodoRepositoryImpl() }
}
@Suppress("UNCHECKED_CAST")
fun <T> Koin.getDependency(clazz: KClasslt;*>): T {
return get(clazz, null) { parametersOf(clazz.simpleName) } as T
}
commonModule - наш общий Koin-модуль с зависимостями для обеих платформ.
initKoin соответственно мы будем дергать из Android-приложения, а initKoinIos - ну вы поняли.
3. в iosMain мы должны добавить свою реализацию метода получения зависимостей, так как на входе у нас будет другой тип. Создаем в этом пакете Koin.kt с кодом:
fun lt;T> Koin.getDependency(objCClass: ObjCClass): T? = getOriginalKotlinClass(objCClass)?.let {
getDependency(it)
}
Теперь вызываем на каждый платформе нужную версию метода init и в целом из основного все.
В Android мы делаем дальше все как обычно, вызывая Koin-ский inject() и добавляя либы Koin-а к проекту. Получаем наш общий репозиторий как обычно:
@Composable
fun MainScreen() {
val repository: TodoRepositoryImpl by inject()
val todos = repository.getAllTodos()
Вы можете заметить, что мы получаем Impl, а не интерфейс и будете правы. Пока что с типами есть проблема. Если указывать в модуле именно интерфейс - краш на iOS, если поменять тип при инжекте в Android - краш на Android. Так что тут есть проблемка над которой стоило бы посидеть.
В iOS мы используем Koin немного иначе.
Создаем Koin.swift с небольшим хелпером:
private var _koin: Koin_coreKoin?
var koin: Koin_coreKoin {
return _koin!
}
func startKoin() {
_koin = KoinKt.doInitKoinIos().koin
}
extension Koin_coreKoin {
func get() -> TodoRepository {
koin.getDependency(objCClass: TodoRepositoryImpl.self) as! TodoRepository
}
}
startKoin мы дергаем в App (как впрочем и на Android)
Обратите внимание, что метод initKoiniOS на iOS стал называться doInitKoinIOS(). Также мы добавляем экстеншен для типа Koin_coreKoin для небольшого сахарка. С помощью этого метода и будем получать нужные зависимости в ContentView:
var repo: TodoRepository
let todos: Arraylt;Todo>
init() {
repo = koin.get()
todos = repo.getAllTodos()
}
С Koin-ом пока наверное закончим.
Примерно так чаще всего реализуется DI в KMM.
Дальше есть мысль попробовать настоящий DI: https://github.com/sergeshustoff/dikt
Хочется получать не краши, а ошибки компиляции в случае проблем с зависимостями. В KMM-проектах это будет экономить кучу времени при отладке!
GitHub
GitHub - sergeshustoff/dikt: Simple and powerful DI for kotlin multiplatform
Simple and powerful DI for kotlin multiplatform. Contribute to sergeshustoff/dikt development by creating an account on GitHub.
👍3
Прикольный репозиторий с типичными вопросами-ответами на Compose:
https://github.com/vinaygaba/Learn-Jetpack-Compose-By-Example
С помощью Preview можно быстро посмотреть есть ли какой-то нужный вам пример или нет.
Кажется, стоит последить.
https://github.com/vinaygaba/Learn-Jetpack-Compose-By-Example
С помощью Preview можно быстро посмотреть есть ли какой-то нужный вам пример или нет.
Кажется, стоит последить.
GitHub
GitHub - vinaygaba/Learn-Jetpack-Compose-By-Example: 🚀 This project contains various examples that show how you would do things…
🚀 This project contains various examples that show how you would do things the "Jetpack Compose" way - vinaygaba/Learn-Jetpack-Compose-By-Example
👍1
Виджет поиска на Compose. Часть 2. Финальная реализация.
Внимательный читатель, который уже повозился с Compose мог заметить, что код из первой части ничего кроме отображения не делал.
То бишь если попытаться что-то ввести в поле - ничего не происходит и текст не отображается.
Почему?
Все очень просто.
Состояние компонента никак не менялось. Соответственно recomposing не делался и повторно не отрисовывался виджет.
Исправить это очень просто. Добавляем состояние, которое будет привязано к введенному тексту:
Все.
Теперь все отрисовывается при каждом вводе.
Давайте посмотрим на весь код и поговорим о реализации и некоторых нюансах:
Продолжение ниже.
Внимательный читатель, который уже повозился с Compose мог заметить, что код из первой части ничего кроме отображения не делал.
То бишь если попытаться что-то ввести в поле - ничего не происходит и текст не отображается.
Почему?
Все очень просто.
Состояние компонента никак не менялось. Соответственно recomposing не делался и повторно не отрисовывался виджет.
Исправить это очень просто. Добавляем состояние, которое будет привязано к введенному тексту:
val text = remember { mutableStateOf(TextFieldValue(value)) }
OutlinedTextField(
value = text.value,
onValueChange = {
text.value = it
textChangedCallback(it.text)
...
}
Все.
Теперь все отрисовывается при каждом вводе.
Давайте посмотрим на весь код и поговорим о реализации и некоторых нюансах:
@ExperimentalUnitApi
@Composable
fun SearchView(
modifier: Modifier = Modifier,
value: String = "",
hintText: String = "",
hintColor: Color = Color(0xFF797F90),
textStyle: TextStyle = TextStyle.Default,
contentDescription: String = "search widget",
cornerRadius: Dp = 12.dp,
showCancelButton: Boolean = false,
cancelButtonText: String = stringResource(R.string.search_view_cancel_button),
textChangedCallback: (String) -> Unit = {},
) {
val text = remember { mutableStateOf(TextFieldValue(value)) }
val focusManager = LocalFocusManager.current
Row(verticalAlignment = Alignment.CenterVertically) {
OutlinedTextField(
modifier = modifier,
value = text.value,
leadingIcon = {
Icon(
modifier = Modifier.size(24.dp),
painter = painterResource(R.drawable.uicit_ic_search),
contentDescription = contentDescription,
)
},
trailingIcon = {
if (text.value.text.isNotEmpty()) {
Icon(
modifier = Modifier.clickable { text.value = TextFieldValue("") },
painter = painterResource(R.drawable.ic_clear),
contentDescription = contentDescription,
)
}
},
singleLine = true,
onValueChange = {
text.value = it
textChangedCallback(it.text)
},
shape = RoundedCornerShape(cornerRadius),
colors = TextFieldDefaults.outlinedTextFieldColors(
placeholderColor = hintColor,
textColor = Color(0xFF383B4F),
cursorColor = Color(0xFF6E7FFC),
focusedBorderColor = Color(0xFF6E7FFC),
unfocusedBorderColor = Color(0xFFDDDEE7),
),
placeholder = {
Text(
text = hintText,
style = TextStyle(fontSize = TextUnit(16.0f, TextUnitType.Sp))
)
},
textStyle = textStyle,
)
if (showCancelButton) {
Spacer(modifier = Modifier.padding(6.dp))
Text(
text = cancelButtonText,
color = Color(0xFF6E7FFC),
maxLines = 1,
modifier = Modifier.clickable {
focusManager.clearFocus()
}
)
}
}
}
Продолжение ниже.
Теперь тезисно о реализации.
* Во-первых, мы переехали с TextField на OutlinedTextField. Это нужно для того, чтобы при фокусе у нас менялся цвет рамки компонента. TextField по-умолчанию так не умеет.
* Далее, modificator мы вынесли во внешний параметр, как и говорили в конце прошлого поста. Компонент нужно и можно настраивать извне.
* Иконка крестика теперь появляется если введен какой-то текст. Вполне логично поведение. А реализовать такое поведение ну очень просто. Добавили нужное условие на text и все.
* По желанию дизайнера мы привнесли в Android чуточку iOS. Увы. Появилась опциональная кнопка Отмена, которая нужна для сбрасывания фокуса. По мне так дичь, но человек настоял. Якобы компоненты мы делаем одинаковыми на обеих платформах.
* Во внешние параметры мы вынесли стиль текста. К слову, стоит немного подрефакторить и вынести туда же настройку цветов. Можно даже передавать в функцию уже настроенный TextFieldColors.
* Для того чтобы компонент и кнопка были единым целым мы вложили его в Row и выровняли по вертикали.
Немного о фокусе ввода.
Как оказалось, есть с ним нюансы и просто так его убрать не получится. На помощь приходит LocalFocusManager и в клике на Отмена мы чистим фокус ввода + скрывается клавиатура.
На текущий момент ок, но фокус убирается у любого компонента, а не только нашего. Возможно с этим стоит повозиться.
В целом это кайф, когда с помощью кода ты можешь добавлять иконку, кастомизировать быстро любое поведение, да и при желании анимацию добавить не сложно.
Какой-то новый уровень создания UI, которого, кажется нам не хватало.
* Во-первых, мы переехали с TextField на OutlinedTextField. Это нужно для того, чтобы при фокусе у нас менялся цвет рамки компонента. TextField по-умолчанию так не умеет.
* Далее, modificator мы вынесли во внешний параметр, как и говорили в конце прошлого поста. Компонент нужно и можно настраивать извне.
* Иконка крестика теперь появляется если введен какой-то текст. Вполне логично поведение. А реализовать такое поведение ну очень просто. Добавили нужное условие на text и все.
* По желанию дизайнера мы привнесли в Android чуточку iOS. Увы. Появилась опциональная кнопка Отмена, которая нужна для сбрасывания фокуса. По мне так дичь, но человек настоял. Якобы компоненты мы делаем одинаковыми на обеих платформах.
* Во внешние параметры мы вынесли стиль текста. К слову, стоит немного подрефакторить и вынести туда же настройку цветов. Можно даже передавать в функцию уже настроенный TextFieldColors.
* Для того чтобы компонент и кнопка были единым целым мы вложили его в Row и выровняли по вертикали.
Немного о фокусе ввода.
Как оказалось, есть с ним нюансы и просто так его убрать не получится. На помощь приходит LocalFocusManager и в клике на Отмена мы чистим фокус ввода + скрывается клавиатура.
На текущий момент ок, но фокус убирается у любого компонента, а не только нашего. Возможно с этим стоит повозиться.
В целом это кайф, когда с помощью кода ты можешь добавлять иконку, кастомизировать быстро любое поведение, да и при желании анимацию добавить не сложно.
Какой-то новый уровень создания UI, которого, кажется нам не хватало.
👍2