Приложение для iOS и Android на Kotlin + Flutter UI

  • Tutorial
image

Вступление


Всем привет. Какое-то время назад, я решил делать свой проект для Android и iOS одновременно. Естественно, встал вопрос о выборе технологий. Пару недель присматривался к популярным стекам и выбрал Kotlin/Native. Поскольку я являюсь Android-разработчиком, то с Kotlin знаком давно, а со Swift особого опыта не было и хотелось получить большую часть кода общего для обеих платформ. Следовательно, сразу встал вопрос, а как писать UI для iOS. Быстрый взгляд на рынок подсказал, что есть Flutter, который позволяет писать UI для двух платформ одновременно. Собственно, так и началась эта история.

В статье описан опыт сборки Flutter в качестве UI и Kotlin для основной логики. Важно: под катом много картинок и инструкция, как собрать проект

Оглавление



Часть 1


Создание общей библиотеки на Kotlin


  1. Выбираем Kotlin Mobile Shared library

    image
  2. Далее

    image
  3. Указываем нашу рабочую папку, здесь я сделал отдельную папку под проект. Поскольку у меня будет 4 разных проекта и их удобнее держать в одном месте

    image
  4. Осталось указать в local.properties путь к sdk.dir и проект начинает собираться, у меня это путь /Users/vlad/Library/Android/sdk

    image
  5. Структура проекта, изменяем имена пакетов с sample на habr.example

    image
  6. Пора приступать к публикации, вызываем wrapper. После этого у нас в проекте появится файлик .gradlew и можно будет с ним работать из терминала

    image
  7. Запускаем из терминала ./gradlew publishToMavenLocal

    image
  8. После этого в локальном maven-репозитории у нас появится 4 папки, в которых будут лежать наши библиотеки

    image

Часть 2


Создание Android-приложения


Создаём android проект

Тут всё стандартно

image

image

image

image

  1. На момент написания статьи проект генерируется с «битой» зависимостью, поэтому убираем в конце jre7, получится koltin-stdlib, после этого проект начинает собираться

    image
  2. Открываем build.gradle и в секцию repositories добавляем mavenLocal. Важно! Секция repositories должна быть та, что внутри allprojects, а не в buildScript

    image
  3. Теперь мы можем добавить нашу библиотеку как зависимость
    implementation 'habr.example:commonLibrary-jvm:0.0.1'

    image
  4. Открываем activity_main.xml и указываем в TextView id main_activity_text

    image
  5. В MainActivity.kt просто устанавливаем текст в этом TextView

    image
  6. Отлично, к этому моменту у нас есть Android приложение, которое умеет использовать функцию hello() из нашей библиотеки
    hello() на эмуляторе
    image

Часть 3


Создаём iOS-проект


  1. Выбираем Single View App

    image
  2. Заполняем основную информацию и выбираем папку. Здесь важно выбирать папку, корневую для наших остальных проектов, поскольку в ней Xcode создаст подпапку с название проекта

    image

    image
  3. Первым делом добавляем CocoaPods. Для этого выполняем pod init в папке проекта, закрываем проект в Xcode и выполняем pod install. Видим, что установка завершилась успешно

    image
  4. Важно! На сайте CocoaPods не рекомендуется добавлять папку /Pods в .gitignore, но я всё равно это сделал. Так как после добавления flutter, мы каждый build будем реконфигурировать зависимости. Пока что, мне это решение нравится больше, чем засорять .git
  5. Открываем проект через файлик Awesome App.xcworkspace

    image
  6. Открываем терминал, в нём переходим в папку нашей commonLibrary и запускаем ./gradlew linkDebugFrameworkIos. После этого в нашей папке build появляется iOSFramework

    image
  7. Выбираем Target

    image
  8. И для этой Target добавляем бинарник

    image
  9. Выбираем Add Other

    image
  10. Указываем путь к framework, который получили на шаге 6 (commonLibrary.framework)

    image

    image
  11. Теперь в проекте у нас должен отображаться этот framework

    image
  12. Идём в Build Settings и отключаем Enable Bitcode

    image
  13. Теперь нужно указать где именно искать наш framework, открываем Framework Search Path

    image
  14. Указываем путь "${PODS_ROOT}/../../commonLibrary". Обязательно выбираем recursive. Конечно, можно обойтись и без этого, если более точно сконфигурировать путь. Но, поскольку это только начало проекта, сейчас нам важно убедиться в том, что вся эта связка заработает. А поменять пути мы можем и потом

    image
  15. Нужно сделать так, чтобы при каждом build в Xcode собирался наш framework при помощи gradle. Открываем Build Phases

    image
  16. Добавляем новую Script Phase

    image
  17. Добавляем код скрипта.

    cd "${PODS_ROOT}/../../commonLibrary"
    echo $(pwd)
    ./gradlew linkIos

    Здесь мы просто переходим в папку проекта нашей библиотеки и запускаем ./gradlew linkIos. Вызов echo $(pwd) нужен только для того, чтобы показать в консоли, в какую конкретно папку мы попали

    image
  18. Сдвигаем нашу build phase в самый верх, сразу после target dependencies

    image
  19. Теперь открываем ViewController и добавляем вызов нашей функции из библиотеки

    image
  20. Запускаем наш проект и видим

    image

Отлично, это значит, что мы правильно подключили Kotlin Library к iOS-проекту
Осталось всего ничего — добавить flutter, как framework для написания UI, в наши приложения и можно начинать разрабатывать продукт

Часть 4


Добавление Flutter в Android-приложение


Тут мне очень помогла статья на github

  1. Переходим в root-папку, где лежат все наши проекты и делаем flutter create -t module flutter_ui

    image
  2. Открываем settings.gradle и включаем наш flutter-модуль, как подпроект

    image
  3. Открываем файл build.gradle и добавляем в зависимости наш проект

    image
  4. Изменяем MainActivity.kt на FlutterActivity

    image
  5. Добавляем App.kt, в котором будем инициализировать Flutter при старте приложения

    image
  6. Изменяем манифест и говорим, что теперь у нас есть класс для Application

    image
  7. Обязательно добавляем java8, без этого flutter не запустится

    image
  8. Видим UI и в логах Hello from JVM, а это значит, что мы собрали вместе UI на Flutter и основную библиотеку на Kotlin/Native



    image
  9. Добавим в MainActivity.kt метод, который мы будем вызывать из Flutter. Здесь по событию из Flutter мы возвращаем наш hello() из kotlin-library

    image
  10. И добавляем в main.dart код, который будет вызывать метод в iOS/Android-части приложения

    image
  11. Получаем


Часть 5


Добавление Flutter в iOS-приложение


  1. Обновляем наш Podfile
    
        flutter_application_path = File.expand_path("../flutter_ui", File.dirname(path))
        eval(File.read(
        File.join(
        flutter_application_path,
        '.ios',
        'Flutter',
        'podhelper.rb')),
        binding)
        

    image

    image
  2. Важно. Добавьте $(inherited) в первую строчку framework search paths. Обязательно проверьте что у вас framework search paths не пустые

    image

    Когда вы меняете зависимости в some/path/my_flutter/pubspec.yaml, вам нужно запустить flutter packages get из some/path/my_flutter, чтобы обновить зависимости в podhelper.rb. После этого нужно запустить pod install из some/path/MyApp
  3. Добавим ещё 1 Build Phase, только уже для Flutter. Выше того, что мы добавляли в части 3 Script phase

    
        "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build
        "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed
        

    image
  4. Заменим наш AppDelegate на FlutterAppDelegate

    image
  5. Обновим ViewController

    image
  6. Обернем наш ViewController в NavigatorController

    image
  7. Теперь приложение запускается. Но, пока что, у нас нет связи между библиотекой и flutter
  8. Добавим эту связь с помощью FlutterMethodChannel

    image
  9. Отлично, теперь и iOS приложение использует flutter для UI и kotlin для основной логики



Заключение


Что важно здесь сказать: я не претендую на то, что вы узнали что-то новое или уникальное. Я просто поделился свои опытом, поскольку для того, чтобы заставить это всё работать вместе, потратил около 4х рабочих дней. И не смог найти примеров кода проекта, который использует одновременно и Kotlin/Native и Flutter

Итоговые проекты

  1. группа проектов
  2. flutter-ui
  3. ios
  4. android
  5. common-library

Список ссылок, которые мне помогли, но не сразу

  1. Сам flutter
  2. Связь между нативным кодом и UI flutter platform-channels
  3. Добавить flutter в существующее приложение github
  4. Kotlin Native native-overview

Only registered users can participate in poll. Log in, please.

Стоит ли развивать тему и писать про архитектуру этого проекта?

  • 97.0%да, мне интересно будет прочитать129
  • 2.3%нет, мне и так хватает материала по теме3
  • 0.8%я сам напишу статью про flutter + kotlin1

Similar posts

Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 21

    0
    А возможно ли вместо котлина на Java писать подобным образом, чтобы кроссплатформенно было?
      0
      Насколько я понимаю нет, потому что Java компилируется только в байт-код для JVM.
      А Kotlin/Native, как раз фича которая позволяет компилировать kotlin-код в бинарники под разные платформы.
        0
        Интересно, как тогда компилируются проекты на LibGDX, которые позволяют писать на Java под iOS?..
          0
          Если я правильно понял то, что написано по этой ссылке, то у LibGDX есть своя виртуальная машина для java которую они запускают при старте приложения на iOS
            0
            Раньше под iOS собирали с помощью RoboVM, но его перестали поддерживать и теперь есть два варианта: опенсорсный форк RoboVM от MobiDevelop (инструкция на гитхабе) или некая Multi-OS Engine.

          0
          Google говорит, что есть примеры запуска java кода на iOS с помощью RoboVM. По идее можно это связать с flutter+dart2. Зависит от RoboVM насколько он работает со свежими релизами iOS
          +4
          А разве не логичнее платформонезависимый код писать на dart, а реализацию платформенных фишек на kotlin/swift? Ведь kotlin native ровно же также не даст пользовать фишки платформы как и dart.
            0
            Почему не даст? Если эти фишки можно использовать из Си, то и из Котлина можно.
              0
              Си? А не слишком ли?
                0
                Насколько я знаю, Kotlin/Native интероперабелен с Си. Насколько с Objective-C и Swift, к сожалению, не знаю.
                  0
                  Со Swift и Objective-C тоже интероперабелен.
              +1
              Основные причины, почему я выбрал kotlin
              • Есть ktor для сетевых запросов, который можно положить в общую бибилиотеку
              • Для меня очень важно что есть LiveData, написанная на Kotlin, это значит, что я могу даже в общей библиотеке использовать reactive-подход
                0
                Если я правильно понял, единственная причина по которой в проекте есть Kotlin/Native это то, что автор знаком с котлином. По хорошему, тут все должно быть на дарте, но все хотят писать на котлине, а не на дарте и как результат — «вот это вот все».
                +2
                Интересно, спасибо.
                Но уж слишком муторно выглядит со стороны пока.
                  0
                  Подскажите, пожалуйста, итоговый размер полученных бинарых файлов, для установки в android и iOS для debug и release конфигураций.
                    0
                    Android
                    • app-debug.apk = 26M
                    • app-release-unsigned.apk = 6.6 Mb


                    0
                    Очень интересно! Что вы использовали вместо java-библиотек? Откуда брали контейнеры, таймера и прочее?
                      0
                      Добрый день. Про какие именно библиотеки идёт речь? Например для сети ktor, для ReactiveX -> LiveData
                        0
                        Насколько читал про kotlin/native, он не может использовать инфраструктуру JVM и, соответственно, все классы java. Остаётся либо чисто котлиновские библиотеки, либо котлиновские порты на нативные библиотеки. Вот и интересуюсь, насколько комфортно использовать Native вне JVM.
                        Сейчас передо мной стоит задача на котлине, и пока в раздумьях — то ли под JVM продолжать, то ли на Native перейти для интереса
                          0
                          Всегда можно сделать expected fun и определить её отдельно для ios/Android
                          Советую попробовать в маленьком проекте и посмотреть, насколько будет удобно
                      0

                      В сентябре 2018 была так же публикация "Fast Prototypes with Flutter + Kotlin/Native":
                      https://tech.olx.com/fast-prototypes-with-flutter-kotlin-native-d7ce5cfeb5f1
                      Было разработано реальное приложение для конференций, но к сожалению исходный код не был выложен (приватная разработка), но автор достаточно подробно рассказал про архитектуру.

                      Only users with full accounts can post comments. Log in, please.