Connect with us

Кроссплатформенная разработка

Как я создал UI-конструктор с помощью Compose Multiplatform, который экспортирует код Compose

Цель Paper — генерировать код, который будет выглядеть так, будто его написал человек. Код должен быть простым в понимании и использовании, поэтому важно сделать его правильно.

Опубликовано

/

     
     

Я создал инструмент проектирования, который экспортирует код, который «как будто написан человеком».

Думаю, вам будет интересно узнать, как я создал это приложение с помощью Compose и как создание такого приложения можно сравнить с созданием приложений для мобильных устройств. Поэтому я опишу здесь, как все прошло.

Причина этого в том, что при переходе от дизайна в Figma к созданию дизайна в коде всегда что-то теряется.

Я видел это слишком много раз, работая с большими и маленькими командами — будь то в Apple, в агентстве по разработке приложений или даже сейчас, когда создаю собственные стартапы.

Почему я выбрал Kotlin и Compose Multiplatform для этого приложения

Мне нужно было, чтобы приложение можно было запустить и распространить через Интернет.

Я рассматривал возможность использования веб-технологий.

Создание интерактивностей с использованием веб-технологий сопряжено с определенными трудностями. Это похоже на работу с обходными путями поверх других обходных путей, а HTML никогда не предназначался для быть интерактивным.

Мне также нужно было экспортировать приложения Compose, поэтому то, что все было написано на Kotlin, чтобы избежать переключения контекста, было большим преимуществом.

Сверхбыстрые циклы разработки с использованием цели Desktop (JVM)

Несмотря на то, что конечной целью было создание веб-приложения, я разработал Paper как приложение для десктопа.

С точки зрения пользовательского опыта (UX) десктоп и веб очень похожи. Однако с точки зрения Compose JVM гораздо быстрее. Намного, намного быстрее.

Нажатие кнопки «Выполнить» в моей IDE занимает несколько секунд, прежде чем мое приложение появляется на экране моего M3 Max, что очень удобно для разработки.

Когда я впервые попробовал это сделать несколько лет назад (до этого я занимался разработкой для Android), я не мог поверить, насколько быстро может работать мое приложение.

Все от начала до конца было создано в виде приложения для ПК. Прямо перед выпуском я добавил в свой проект веб-таргет, чтобы он заработал.

Такая разработка была очень продуктивной. Я получил скорость JVM, не беспокоясь о многоплатформенности, и только при необходимости создал соответствующие expect/actual интерфейсы, чтобы все заработало.

При переходе от одноплатформенной к многоплатформенной версии возникают некоторые сложности, поскольку иногда для нужного вам API нет однозначного соответствия (например, как перейти от полного доступа к файловой системе на десктопе к отсутствию или ограниченному доступу к ней в браузере). Однако это выходит за рамки данной статьи, так как это отдельная тема.

Различия между разработкой для десктопов и мобильных устройств

Преимущество разработки для ПК заключается в том, что вы не работаете с ограниченными ресурсами, как на мобильных устройствах.

Вам не нужно беспокоиться об изменениях конфигурации, низком заряде батареи, фоновых задачах и т.д. Вы просто делаете.

Это значительно упрощает код. Я давно перестал использовать ViewModel и просто использую remember {}, чтобы сохранить любое состояние, которое мне может понадобиться.

Paper — это статическое приложение, то есть генерация кода происходит внутри приложения (без участия бэкэнда). Это также упрощает ситуацию, поскольку вся логика обрабатывается через корутины.

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

Я работаю над пользовательским интерфейсом и занимаюсь дизайном и программированием всех своих приложений, поэтому отсутствие ограничений, накладываемых правилами платформы, является для меня хорошей вещью (хотя, конечно, это требует определенных усилий). Я также создал библиотеку нестилизованных компонентов для Compose, которую я использую во всех своих приложениях (включая Paper) для значительного повышения производительности (она с открытым исходным кодом и называется Compose Unstyled).

Неприятные стороны Compose Multiplatform

Экосистема еще молода. На самом деле это не проблема, поскольку зачастую можно схитрить, используя базовую платформу. Например, в Kotlin нет полноценной библиотеки ZIP, поэтому я использую JSZip в браузерt и Java ZIP API в JVM.

Настоящая проблема возникает, когда вам нужно сделать что-то утомительное в самом Compose, но для этого нет официального API или сторонней библиотеки.

В моем случае мне пришлось создать собственное решение для перетаскивания с нуля, поскольку API Compose не поддерживали все необходимые мне варианты использования UX, а сторонние решения были слишком простыми.

Это была огромная трата времени, и я бы не хотел, чтобы мне пришлось через это проходить снова. Но эй, в жизни каждый сам выбирает себе яд. Я хорошо разбираюсь в интерфейсах, поэтому из всех вариантов этот был наименее плохим.

Генерация кода Kotlin с использованием Kotlin

Цель Paper — генерировать код, который будет выглядеть так, будто его написал человек. Код должен быть простым в понимании и использовании, поэтому важно сделать его правильно.

Я рассматривал решения с открытым исходным кодом, но они показались мне слишком перегруженными Java-программированием, чтобы доверять им. Если бы это был какой-либо другой проект, я бы, вероятно, использовал их, но мне нужен был способ генерации гибкого кода (например, объединение модификаторов Compose или обработка адаптивного дизайна).

Сосредоточившись на том, что необходимо охватить на каждом этапе, я в итоге разработал собственное решение. После множества итераций это выглядит так:

val contentPadding = (baseProperties.removePaddingValues() ?: NoPadding)
    .withOverride(
        override = tabletOverrides.removePaddingValues() ?: NoPadding,
        default = NoPadding
    ).dropIf { it == NoPadding }


FunctionCall(
    fullName = LazyColumn,
    params = listOfNotNull(
        verticalArrangement(alignmentSameAxis, spacing),
        horizontalAlignmentParameter(alignmentOtherAxis, "horizontalAlignment"),
        FunParameter("contentPadding", contentPadding),
        Modifier(baseProperties, overrides)
    ),
    trailingLambda = lazyLayoutContents(LazyListScope)
)

Что генерирует следующий код:

package com.example

import androidx.compose.runtime.Composable
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.ui.unit.dp
import com.builtwithpaper.currentScreenWidthBreakpoint
import com.builtwithpaper.isAtLeast
import com.builtwithpaper.ScreenWidthBreakpoint

@Composable
fun NewScreen() {
    val breakpoint = currentScreenWidthBreakpoint()

    LazyColumn(
        contentPadding = if (breakpoint isAtLeast ScreenWidthBreakpoint.Small) PaddingValues(32.dp) else PaddingValues(
            16.dp
        )
    ) {
    }
}
Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.
Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: