Есть ли опыт работы с Kotlin Multiplatform (KMP)?

Ответ

Да, у меня был опыт интеграции Kotlin Multiplatform (KMP) модуля в Flutter-приложение. Мы использовали KMP для написания общей бизнес-логики (например, валидации, моделей данных, некоторых репозиториев) на Kotlin, которую затем могли использовать и в нативной Android-части, и во Flutter.

Архитектура: В гибридном проекте UI был реализован на Flutter (единый для Android и iOS), а сложная доменная логика, общая с нативным Android-модулем, вынесена в KMP-модуль.

Интеграция KMP с Flutter:

  1. KMP-модуль компилируется в Android-библиотеку (.aar) и iOS-фреймворк (.framework).
  2. Для Android: KMP-модуль добавляется как зависимость в android/build.gradle проекта Flutter.
  3. Для iOS: Сгенерированный фреймворк подключается к iOS-части Flutter-проекта через CocoaPods или вручную.
  4. Во Flutter-коде общая логика вызывается через platform channels.

Пример вызова KMP-кода из Flutter (через MethodChannel):

// Flutter-сторона (Dart)
import 'package:flutter/services.dart';

class KmpService {
  static const platform = MethodChannel('com.example.kmp/channel');

  Future<String> performBusinessLogic(String input) async {
    try {
      final result = await platform.invokeMethod('processData', input);
      return result;
    } on PlatformException catch (e) {
      return "Error: '${e.message}'.";
    }
  }
}
// KMP / Android-сторона (Kotlin)
// В MainActivity.kt Flutter-проекта
class MainActivity : FlutterActivity() {
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "com.example.kmp/channel")
            .setMethodCallHandler { call, result ->
                if (call.method == "processData") {
                    val input = call.arguments as String
                    // Вызов функции из общего KMP-кода!
                    val output = SharedBusinessLogic.process(input)
                    result.success(output)
                } else {
                    result.notImplemented()
                }
            }
    }
}

Плюсы подхода:

  • Переиспользование кода: Сложная и критичная логика пишется один раз на Kotlin и работает на всех платформах.
  • Безопасность и производительность: Kotlin — статически типизированный и производительный язык.

Сложности:

  • Усложнение сборки: Необходимо настраивать сборку KMP-модуля и его интеграцию в Flutter.
  • Оверхеад коммуникации: Вызовы через platform channel асинхронны и имеют некоторую задержку, что не подходит для высокочастотных операций.

Этот опыт показал, что KMP — мощный инструмент для sharing-кода, но его интеграция с Flutter требует тщательной архитектурной проработки.

Ответ 18+ 🔞

А, ну так вот, история про то, как мы впихнули Котлин Мультиплатформу во Флаттер. Ебать мои старые костыли, это был тот ещё опыт, чувак. В общем, была у нас идея — всякую мозговую, доменную логику (типа валидаций, моделек, репозиториев) писать один раз, а таскать её и в нативный андроид, и во флаттер. Ну, чтобы не дублировать, понимаешь? Овердохуища работы было бы.

Как это всё было устроено: Вся красота интерфейса — на Флаттере, один для всех. А вот под капотом, в самом сердце, где всё думает и решает — там сидел наш общий КМП-модуль, как какой-нибудь хитрая жопа.

Как мы это прикручивали:

  1. КМП-шный модуль мы настраивали так, чтобы он выплевывал на выходе библиотечку для андроида (.aar) и фреймворк для айос (.framework).
  2. Для Андроида: Просто пихали эту библиотеку как зависимость в android/build.gradle флаттеровского проекта. Вроде ничего сложного.
  3. Для iOS: Тут уже веселее. Сгенерированный фреймворк надо было прицепить к айосовской части проекта. Через CocoaPods или вручную — кому как нравится, но волнение ебать, пока не заработает.
  4. А чтобы позвать эту логику из Флаттера — использовали старые добрые platform channels. Ну, мост такой, понимаешь.

Вот, смотри, как выглядел вызов (примерно):

// Со стороны Флаттера (Dart)
import 'package:flutter/services.dart';

class KmpService {
  static const platform = MethodChannel('com.example.kmp/channel');

  Future<String> performBusinessLogic(String input) async {
    try {
      final result = await platform.invokeMethod('processData', input);
      return result;
    } on PlatformException catch (e) {
      return "Error: '${e.message}'.";
    }
  }
}
// Со стороны КМП / Андроида (Kotlin)
// Писали это в MainActivity.kt флаттеровского проекта
class MainActivity : FlutterActivity() {
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "com.example.kmp/channel")
            .setMethodCallHandler { call, result ->
                if (call.method == "processData") {
                    val input = call.arguments as String
                    // А вот и магия! Зовём функцию из общего КМП-кода!
                    val output = SharedBusinessLogic.process(input)
                    result.success(output)
                } else {
                    result.notImplemented()
                }
            }
    }
}

Что в итоге было круто:

  • Код не повторялся: Написали сложную логику один раз на Котлине — и она работает везде. Красота.
  • Надёжность и скорость: Котлин — язык строгий, статический, без этих ваших динамических плясок. Работает чётко.

А что было пиздец как сложно:

  • Сборка превратилась в ёперный театр: Настроить сборку КМП-модуля и его интеграцию — это отдельный квест на терпение. Терпения ноль ебать к концу настройки.
  • Задержки в общении: Вызовы через platform channel — они асинхронные, с небольшой, но задержкой. Для высокочастотных штук, где каждый миллисекунд на счету, это не очень.

В общем и целом, опыт показал, что КМП — инструмент мощный, реально позволяет переиспользовать код по-взрослому. Но прилепить его к Флаттеру — это вам не хуй с горы скатить, тут надо головой думать и архитектуру продумывать заранее, а то потом сам от себя охуеешь.