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

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 проект

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


  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

    rb-02uakaz1-62vubrmax6g0bso.png

    image

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

    image

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

    image

  11. Получаем
    1i498vcqirringj5rr8sz1cbmyw.png

Часть 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

    imageulyfwu_khrnskwnggz4-aeianma.png

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

    image

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

    mqssjg0c9ln11i-_vik9qxjwejc.png


Заключение


Что важно здесь сказать: я не претендую на то, что вы узнали что-то новое или уникальное. Я просто поделился свои опытом, поскольку для того, чтобы заставить это всё работать вместе, потратил около 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

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

© Habrahabr.ru