Для быстрого ознакомления:

  • Что такое Native Assets - объяснение для новичков

  • История и эволюция

  • Архитектура системы

  • Build Hooks - сердце системы

  • Практические примеры

  • Продвинутые концепции

  • Лучшие практики

  • Troubleshooting и отладка

  • Экосистема и дальнейшее развитие

  • Реальные кейсы использования

  • Производительность и оптимизация

Что такое Native Assets - объяснение для новичков

Простыми словами

Представьте, что у вас есть Dart-программа, и вы хотите использовать готовую библиотеку, написанную на C, C++, Rust или другом языке. Раньше это было сложно - нужно было вручную компилировать библиотеку, следить за тем, чтобы она попала в нужное место, и писать много дополнительного кода.

Native Assets - это система, которая автоматизирует весь этот процесс. Она позволяет вашему Dart-пакету "включать в себя" нативный код и автоматически его компилировать и подключать.

Аналогия из жизни

Это как заказ еды с доставкой:

  • Раньше: Вы сами покупали продукты, готовили, мыли посуду.

  • С Native Assets: Вы просто говорите "хочу пиццу", а система сама заказывает, доставляет и даже убирает за собой.

Техническое определение

Native Assets — это официальная система в Dart, которая позволяет пакетам содержать не только Dart-код, но и исходный код на других языках (C, C++, Rust и др.). Система автоматически управляет сборкой этого нативного кода в динамические библиотеки (.so, .dll, .dylib), их упаковкой в приложение и обеспечивает прозрачный доступ к ним из Dart-кода во время выполнения через FFI (Foreign Function Interface).

История и эволюция

До Native Assets (темные времена)

Разработчик хочет использовать C-библиотеку:

  1. Вручную компилирует библиотеку для каждой платформы.

  2. Копирует .so/.dll/.dylib файлы в правильные места.

  3. Пишет FFI-биндинги.

  4. Молится, чтобы все работало на разных устройствах.

  5. Повторяет все это при каждом обновлении.

С Native Assets (светлое будущее)

Разработчик хочет использовать C-библиотеку:

  1. Добавляет зависимость в pubspec.yaml.

  2. Описывает процесс сборки в hook/build.dart (один раз).

  3. Система автоматически все компилирует и подключает.

  4. Работает везде "из коробки".

Timeline развития

  • Dart 3.2: Появилась первая версия Native Assets за экспериментальным флагом --enable-experiment=native-assets.

  • Dart 3.4 (Май 2024): Стабилизация Native Assets. Функция стала доступна по умолчанию, флаг больше не требуется.

  • Flutter 3.22 (Май 2024): Интеграция Native Assets во Flutter. Система стала официально поддерживаться для сборки приложений на всех платформах.

Архитектура системы

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Dart Package  │───▶│   Build Hooks   │───▶│  Native Assets  │
│                 │    │                 │    │                 │
│ - pubspec.yaml  │    │ - hook/build.dart│   │ - .so/.dll/.dylib│
│ - lib/*.dart    │    │                  │   │ - metadata.json │
│ - src/*.c       │    │                  │   │                 │
└─────────────────┘    └─────────────────┘    └─────────────────┘
  1. Package Structure

    my_native_package/
    ├── pubspec.yaml          # Зависимости и конфигурация пакета
    ├── lib/
    │   └── my_package.dart   # Dart API для взаимодействия с нативным кодом
    ├── src/
    │   ├── my_lib.c          # Нативный исходный код
    │   └── my_lib.h          # Заголовочные файлы
    └── hook/
        └── build.dart        # Скрипт-инструкция по сборке нативного кода
    
  2. Build System Flow

    1. dart pub get или flutter build обнаруживает наличие hook/build.dart.

    2. Запускает этот скрипт (build hook) в изолированной среде для каждой целевой платформы.

    3. Hook компилирует нативный код, используя системные компиляторы (GCC, Clang, MSVC) или специализированные инструменты (Cargo, CMake).

    4. Hook генерирует метаданные о созданных ассетах (библиотеках).

    5. Система сборки упаковывает эти ассеты вместе с приложением.

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

Build Hooks - сердце системы

Что такое Build Hook

Build Hook — это специальный Dart-скрипт (hook/build.dart), который говорит системе сборки:

  • Какой нативный код нужно скомпилировать.

  • Как именно его компилировать (какие флаги, зависимости).

  • Где разместить результат.

Простой пример hook/build.dart (для демонстрации)

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

// hook/build.dart
import 'package:native_assets_cli/native_assets_cli.dart';
import 'dart:io';

void main(List<String> args) async {
  await build(args, (config, output) async {
    // Определяем, что мы хотим скомпилировать
    final packageName = config.packageName;
    final sourceFile = config.packageRoot.resolve('src/my_lib.c');
    final outDir = config.outputDirectory;

    // Имя библиотеки зависит от платформы
    String libName;
    if (config.targetOS == OS.windows) {
      libName = 'my_lib.dll';
    } else if (config.targetOS == OS.macOS) {
      libName = 'libmy_lib.dylib';
    } else {
      libName = 'libmy_lib.so';
    }
    final outFile = outDir.resolve(libName);

    // Компилируем C код в динамическую библиотеку
    final result = await Process.run(
      'gcc',
      [
        '-shared',
        '-fPIC',
        '-o',
        outFile.toFilePath(),
        sourceFile.toFilePath(),
      ],
    );

    if (result.exitCode != 0) {
      throw Exception('Compilation failed: ${result.stderr}');
    }

    // Сообщаем системе о созданной библиотеке
    output.addAsset(NativeCodeAsset(
      package: packageName,
      name: 'src/my_lib.c', // Имя ассета должно соответствовать пути к исходнику
      file: outFile,
      linkMode: LinkMode.dynamic,
      os: config.targetOS,
      architecture: config.targetArchitecture,
    ));
  });
}

Продвинутый и рекомендуемый пример с native_toolchain_c

Этот пакет предоставляет удобный CBuilder для кроссплатформенной компиляции.

// hook/build.dart
import 'package:native_assets_cli/native_assets_cli.dart';
import 'package:native_toolchain_c/native_toolchain_c.dart';

void main(List<String> args) async {
  await build(args, (config, output) async {
    final cbuilder = CBuilder.library(
      name: 'my_complex_lib',
      assetName: 'path/to/my_lib.dart', // Путь к Dart файлу, который будет использовать библиотеку
      sources: [
        'src/core.c',
        'src/utils.c',
      ],
      includes: [
        'src/include/',
      ],
      defines: {
        'VERSION': '1.0.0',
        'DEBUG': config.buildMode == BuildMode.debug ? '1' : '0',
      },
    );

    await cbuilder.run(
      buildConfig: config,
      buildOutput: output,
      // Для отладки можно добавить логгер
      // logger: Logger()..level = Level.all,
    );
  });
}

Практические примеры

Пример 1: Простая математическая библиотека

Структура проекта

math_native/
├── pubspec.yaml
├── lib/
│   └── math_native.dart
├── src/
│   ├── math_ops.c
│   └── math_ops.h
└── hook/
    └── build.dart

C-код (src/math_ops.c) (C нет в редакторе хабра в выпадающем списке языков, поставил С++)

// src/math_ops.c
#include "math_ops.h"

// Простой итеративный Фибоначчи
int fibonacci(int n) {
    if (n <= 1) return n;
    int a = 0, b = 1, c;
    for (int i = 2; i <= n; i++) {
        c = a + b;
        a = b;
        b = c;
    }
    return b;
}

Заголовочный файл (src/math_ops.h)

// src/math_ops.h
#ifndef MATH_OPS_H
#define MATH_OPS_H

// Dart FFI требует явного указания видимости для Windows
#if defined(_WIN32)
#define API __declspec(dllexport)
#else
#define API
#endif

API int fibonacci(int n);

#endif

Build Hook (hook/build.dart)

// hook/build.dart
// Примечание: для этого кода нужно добавить пакет native_toolchain_rust
import 'package.native_assets_cli/native_assets_cli.dart';
import 'package.native_toolchain_rust/native_toolchain_rust.dart';

void main(List<String> args) async {
  await build(args, (config, output) async {
    // Создаем сборщик для Rust-библиотеки
    final rustBuilder = RustBuilder.library(
      name: 'string_processor', // Имя вашего пакета из файла Cargo.toml
      // Указываем Dart-файл, который будет использовать эту библиотеку
      assetName: 'package:имя_вашего_пакета/имя_dart_файла.dart',
    );

    // Запускаем сборку
    await rustBuilder.run(
      buildConfig: config,
      buildOutput: output,
    );
  });
}

Dart API (lib/math_native.dart)

// lib/math_native.dart
import 'dart:ffi';

// Аннотация @Native указывает FFI на имя нативной функции.
// Система сборки Native Assets автоматически найдет и загрузит
// нужную библиотеку, скомпилированную для текущей платформы.
@Native<Int32 Function(Int32)>('fibonacci')
external int _fibonacci(int n);

/// Вычисление числа Фибоначчи с использованием нативной реализации.
int fibonacci(int n) => _fibonacci(n);

pubspec.yaml

name: math_native
description: Fast native math operations.
version: 1.0.0
publish_to: 'none' # Для локального примера

environment:
  sdk: '>=3.4.0 <4.0.0'

dependencies:
  ffi: ^2.1.0

# Пакеты для сборки теперь не нужно указывать,
# Dart SDK находит и использует их автоматически.
# dev_dependencies:
#   native_assets_cli: ...
#   native_toolchain_c: ...

Пример 2: Интеграция с Rust

Rust-код (src/lib.rs)

// src/lib.rs
use std::ffi::{CStr, CString};
use std::os::raw::c_char;

#[no_mangle]
pub extern "C" fn process_string(input: *const c_char) -> *mut c_char {
    let c_str = unsafe { CStr::from_ptr(input) };
    let rust_string = c_str.to_string_lossy();

    // Обработка строки в Rust (реверс и верхний регистр)
    let processed = rust_string.chars().rev().collect::<String>().to_uppercase();

    let c_string = CString::new(processed).unwrap();
    c_string.into_raw()
}

#[no_mangle]
pub extern "C" fn free_string(ptr: *mut c_char) {
    if !ptr.is_null() {
        unsafe {
            // Восстанавливаем CString из указателя и освобождаем память
            let _ = CString::from_raw(ptr);
        };
    }
}

Cargo.toml

Ini, TOML (TOML нет в редакторе хабра в выпадающем списке языков)

[package]
name = "string_processor"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

Build Hook для Rust (hook/build.dart)

// hook/build.dart
import 'dart:io';
import 'package:native_assets_cli/native_assets_cli.dart';

void main(List<String> args) async {
  await build(args, (config, output) async {
    // Вызов Cargo для сборки Rust проекта
    final result = await Process.run(
      'cargo',
      ['build', '--release'],
      workingDirectory: config.packageRoot.toFilePath(),
    );

    if (result.exitCode != 0) {
      throw Exception('Rust build failed: ${result.stderr}');
    }

    // Находим путь к скомпилированной библиотеке
    // ... логика поиска .dll/.so/.dylib в target/release/ ...
    // Для простоты примера, опустим эту часть

    // Добавляем ассет в вывод (в реальном коде нужно найти файл)
    // output.addAsset(...)
  });
}
// Примечание: для Rust существуют более удобные пакеты-обертки,
// такие как `native_toolchain_rust`, которые автоматизируют этот процесс.

Продвинутые концепции

1. Условная компиляция

Build hook может анализировать конфигурацию сборки (config) и включать разные исходники или флаги.

// hook/build.dart
void main(List<String> args) async {
  await build(args, (config, output) async {
    final sources = <String>['src/core.c'];
    final defines = <String, String>{};

    // Платформо-зависимый код
    switch (config.targetOS) {
      case OS.windows:
        sources.add('src/platform/windows.c');
        defines['PLATFORM_WINDOWS'] = '1';
        break;
      case OS.linux:
        sources.add('src/platform/linux.c');
        defines['PLATFORM_LINUX'] = '1';
        break;
      case OS.macOS:
        sources.add('src/platform/macos.c');
        defines['PLATFORM_MACOS'] = '1';
        break;
      default:
        // Обработка неподдерживаемых платформ
    }

    final cbuilder = CBuilder.library(
      name: 'cross_platform_lib',
      assetName: 'package:my_package/my_package.dart',
      sources: sources,
      defines: defines,
    );

    await cbuilder.run(buildConfig: config, buildOutput: output);
  });
}

2. Управление зависимостями

В build hook можно скачивать и компилировать сторонние библиотеки.

// hook/build.dart
// (Концептуальный пример)
void main(List<String> args) async {
  await build(args, (config, output) async {
    final depsDir = config.outputDirectory.resolve('deps');

    // Скачиваем и распаковываем зависимость (например, libjpeg)
    if (!await Directory.fromUri(depsDir).exists()) {
      await downloadAndExtract(
        url: 'https://example.com/libjpeg.tar.gz',
        destination: depsDir,
      );
      // Запускаем ./configure && make для сборки зависимости
      await buildDependency(depsDir);
    }

    final cbuilder = CBuilder.library(
      name: 'my_image_lib',
      assetName: 'package:my_package/my_package.dart',
      sources: ['src/image_utils.c'],
      includes: [
        'src/',
        depsDir.resolve('include').path,
      ],
      // Линкуемся со статически собранной зависимостью
      libraries: [
        depsDir.resolve('lib/libjpeg.a').path,
      ],
    );

    await cbuilder.run(buildConfig: config, buildOutput: output);
  });
}

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

Лучшие практики

1. Структура проекта

Хорошо организованный проект облегчает поддержку.

my_native_package/
├── pubspec.yaml
├── README.md
├── lib/
│   ├── my_package.dart          # Публичный Dart API
│   └── src/
│       ├── bindings.dart        # FFI-биндинги (@Native)
│       └── exceptions.dart      # Кастомные исключения
├── src/                         # Нативный код
│   ├── core/
│   │   ├── api.h                # Публичный C API
│   │   └── api.c
│   └── platform/                # Платформо-специфичный код
└── hook/
    └── build.dart               # Build hook

2. Обработка ошибок в Build Hooks

Build hook должен быть надежным и предоставлять понятные сообщения об ошибках.

// hook/build.dart
void main(List<String> args) async {
  await build(args, (config, output) async {
    try {
      // Проверяем наличие необходимых инструментов (например, CMake)
      await _ensureToolExists('cmake');

      final cbuilder = CBuilder.library(...);
      await cbuilder.run(
        buildConfig: config,
        buildOutput: output,
        logger: Logger.root, // Включаем логирование
      );

    } on ToolNotFoundException catch (e) {
      print('Ошибка сборки: ${e.message}');
      print('Рекомендация: Установите ${e.toolName} и добавьте в PATH.');
      rethrow;
    } catch (e, stackTrace) {
      print('Непредвиденная ошибка во время сборки: $e');
      print('Стек вызовов: $stackTrace');
      rethrow;
    }
  });
}
// Вспомогательная функция _ensureToolExists и исключение ToolNotFoundException

3. Кроссплатформенная совместимость в Dart

Правильный подход: С Native Assets вам не нужно писать код для загрузки библиотек под разные платформы в Dart. Эта логика полностью находится в hook/build.dart. Dart-код остается чистым и платформо-независимым.

Устаревший подход (НЕ ИСПОЛЬЗОВАТЬ С NATIVE ASSETS):

// НЕПРАВИЛЬНО: Ручная загрузка библиотеки
if (Platform.isWindows) {
  DynamicLibrary.open('my_lib.dll');
} // ... и т.д.

Правильный Dart-код:

// lib/src/bindings.dart
import 'dart:ffi';

// Этот код будет работать на Windows, macOS, Linux, Android и iOS
// без каких-либо изменений.
@Native<Int32 Function(Int32)>('my_function')
external int _myFunction(int value);

class MyLib {
  static int myFunction(int value) {
    try {
      return _myFunction(value);
    } catch (e) {
      // Можно обернуть ошибку FFI в кастомное исключение
      throw NativeException('Failed to call my_function: $e');
    }
  }
}

class NativeException implements Exception {
  final String message;
  NativeException(this.message);
}

4. Тестирование Native Assets

Тесты должны проверять как успешное выполнение, так и обработку ошибок.

// test/native_test.dart
import 'package:test/test.dart';
import 'package:math_native/math_native.dart'; // Импортируем наш пакет

void main() {
  group('Native Fibonacci Tests', () {
    test('should return correct values for base cases', () {
      expect(fibonacci(0), 0);
      expect(fibonacci(1), 1);
    });

    test('should calculate fibonacci correctly for a small number', () {
      expect(fibonacci(10), 55);
    });

    // Можно добавить тесты на производительность
    test('should execute within reasonable time', () {
      final stopwatch = Stopwatch()..start();
      fibonacci(40); // Достаточно большая нагрузка
      stopwatch.stop();
      expect(stopwatch.elapsedMilliseconds, lessThan(100));
    });
  });
}

Troubleshooting и отладка

Частые проблемы и решения

  1. Сборка падает с непонятной ошибкой

    • Решение: Запустите сборку с максимальной детализацией логов: flutter build <platform> -v. Ищите ошибки от компилятора (Clang, GCC, MSVC) или от вашего build hook.

  2. UnsatisfiedLinkError или Lookup failed

    • Причина: Dart FFI не может найти нативную функцию.

    • Решение:

      • Проверьте, что имя функции в аннотации @Native<...> ('my_function') точно совпадает с именем в нативном коде.

      • Для C++ убедитесь, что функция обернута в extern "C".

      • Для Windows убедитесь, что функция экспортируется с помощью __declspec(dllexport).

      • Проверьте assetName в CBuilder.library() в hook/build.dart. Он должен указывать на Dart-файл, где используется @Native.

  3. Build Hook не выполняется

    • Проверьте:

      • Файл точно называется build.dart и находится в папке hook в корне пакета.

      • Выполните dart pub get или flutter clean и flutter pub get, чтобы система сборки заново обнаружила hook (горячая перезагрузка здесь не работает).

Экосистема и дальнейшее развитие

Текущие возможности (не будущее)

  • Декларативные биндинги: Аннотация @Native<...> является основным способом связывания с нативным кодом.

  • Интеграция с Flutter: Native Assets полностью поддерживаются во Flutter для сборки на Android, iOS, Windows, macOS и Linux.

  • Интеграция с IDE: IDE (VS Code, Android Studio) предоставляют базовую поддержку, включая подсветку синтаксиса и возможность отладки Dart-кода. Прямой переход к нативному коду пока ограничен.

  • Сторонние инструменты: Сообщество активно развивает пакеты-обертки для популярных систем сборки (native_toolchain_c, native_toolchain_cmake, native_toolchain_rust), которые значительно упрощают написание build-хуков.

Планируемые улучшения

  • Генерация биндингов: Инструменты, такие как package:ffigen, адаптируются для работы с Native Assets, что позволит автоматически генерировать Dart-код из C/C++ заголовочных файлов.

  • Улучшенная отладка: Работа над возможностью бесшовной отладки с переходом между Dart и нативным кодом.

  • Поддержка WebAssembly (Wasm): Интеграция с Wasm позволит использовать тот же нативный код (скомпилированный в Wasm) в веб-приложениях.

Реальные кейсы использования

Кейс 1: Высокопроизводительная обработка изображений

Проблема: Приложение для обработки фотографий работало медленно из-за операций над пикселями в Dart-коде.

Решение с Native Assets: Вынести ресурсоемкие алгоритмы (фильтры, размытие) в C/C++ и вызывать их через FFI.

Dart-код (c корректным FFI):

// lib/image_processor.dart
import 'dart:ffi';
import 'dart:typed_data';
import 'package:ffi/ffi.dart';

@Native<Void Function(Pointer<Uint8>, Int32, Int32, Double)>('apply_sepia_filter')
external void _applySepiaFilter(Pointer<Uint8> imageData, int width, int height, double intensity);

void applySepiaFilter(Uint8List imageData, int width, int height, {double intensity = 1.0}) {
  // Выделяем память в нативной куче
  final pointer = calloc<Uint8>(imageData.length);
  // Копируем данные из Dart-массива в нативную память
  pointer.asTypedList(imageData.length).setAll(0, imageData);

  try {
    // Вызываем быструю нативную функцию
    _applySepiaFilter(pointer, width, height, intensity);
    // Копируем результат обратно в Dart-массив
    imageData.setAll(0, pointer.asTypedList(imageData.length));
  } finally {
    // Обязательно освобождаем память
    calloc.free(pointer);
  }
}

Нативный код для фильтра apply_sepia_filter пишется на C.

Результат: Обработка изображений становится в теории в 10-15 раз быстрее, но у меня ускорение в пределах от 3 до 8 раз, если кто-то в курсе что я делаю не так - пожалуйста прокоментируйте.

Кейс 2: Интеграция с существующей C++ библиотекой

Проблема: Есть готовая, протестированная C++ библиотека для работы с 3D-геометрией, которую нужно использовать в Dart-приложении.

Решение: Создать тонкую C-обертку (wrapper) для C++ кода и подключить ее через Native Assets.

Обертка C API (src/geometry_wrapper.cpp)

#include "geometry_lib.hpp" // Существующая C++ библиотека

// extern "C" отключает C++ name mangling, делая функции видимыми для C FFI
extern "C" {
    API double calculate_distance(Point3D* p1, Point3D* p2) {
        // Кастуем указатели к C++ типам и вызываем C++ код
        auto* point1 = reinterpret_cast<geometry::Point3D*>(p1);
        auto* point2 = reinterpret_cast<geometry::Point3D*>(p2);
        return geometry::distance(*point1, *point2);
    }
}

Build hook для этого будет компилировать C++ файлы с помощью g++ или clang++.

Производительность и оптимизация

Benchmarking Native Assets

Используйте package:benchmark_harness для сравнения производительности Dart и нативной реализации.

// benchmark/performance_test.dart
import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:my_native_package/my_native_package.dart'; // Наш пакет

// Benchmark для нативной реализации
class NativeBenchmark extends BenchmarkBase {
  NativeBenchmark() : super('MatrixMultiplication.Native');
  @override
  void run() => nativeMatrixMultiply(); // вызов нативной функции
}

// Benchmark для Dart-реализации
class DartBenchmark extends BenchmarkBase {
  DartBenchmark() : super('MatrixMultiplication.Dart');
  @override
  void run() => dartMatrixMultiply(); // вызов Dart-функции
}

void main() {
  NativeBenchmark().report();
  DartBenchmark().report();
}

Оптимизация Build Hooks

Для ускорения сборки и повышения производительности нативного кода:

  • Используйте флаги оптимизации: В hook/build.dart для релизных сборок добавляйте флаги -O3 (максимальная оптимизация) и -flto (Link Time Optimization).

  • Кэширование: Реализуйте в build hook логику кэширования. Если исходные файлы не изменились, используйте уже скомпилированные артефакты вместо повторной сборки.

  • Платформо-специфичные флаги: Используйте флаги, оптимизированные для конкретных архитектур, например -march=native.

  • Проверьте assetName в CBuilder.library() в файле hook/build.dart. Он должен точно указывать на тот Dart-файл, где используется аннотация @Native. Если вы используете @Native в файле lib/src/bindings.dart, то и assetName должен быть 'package:имя_пакета/src/bindings.dart'.

// hook/build.dart

// Функция для получения флагов компилятора C
List<String> _getOptimizedCFlags(BuildMode buildMode) {
  if (buildMode == BuildMode.release) {
    return ['-O3', '-DNDEBUG']; // Оптимизация и отключение assert'ов
  }
  return ['-g', '-DDEBUG']; // Флаги для отладки
}

// Функция для получения флагов компоновщика
List<String> _getOptimizedLdFlags(BuildMode buildMode) {
  if (buildMode == BuildMode.release) {
    return ['-flto']; // Оптимизация на этапе компоновки
  }
  return [];
}

void main(List<String> args) async {
  await build(args, (config, output) async {
    final cbuilder = CBuilder.library(
      name: 'my_optimized_lib',
      assetName: 'package:my_package/my_package.dart',
      sources: ['src/my_lib.c'],
      // Правильная передача флагов
      cFlags: _getOptimizedCFlags(config.buildMode),
      ldFlags: _getOptimizedLdFlags(config.buildMode),
    );
    
    await cbuilder.run(buildConfig: config, buildOutput: output);
  });
}

Заключение

Dart Native Assets представляют собой революционное решение для интеграции нативного кода. Эта система решает множество проблем, с которыми сталкивались разработчики при работе с FFI, и является стабильным, мощным инструментом.

Ключевые преимущества

  • Автоматизация: Полностью автоматизированный процесс компиляции и подключения нативных библиотек.

  • Кроссплатформенность: Единый build-скрипт для сборки под все целевые платформы (мобильные, десктопные).

  • Производительность: Прямые вызовы нативных функций без накладных расходов, присущих Method Channels.

  • Простота: Значительно упрощенный процесс разработки и поддержки пакетов с нативным кодом.

На 2025 год система Native Assets является зрелой и рекомендованной для всех задач, требующих высокой производительности или интеграции с существующими C/C++/Rust библиотеками в экосистеме Dart и Flutter.

Комментарии (4)


  1. Alex283
    15.06.2025 15:06

    "Кто бы мог думать, ваше превосходительство, что человеческая пища, в первоначальном виде, летает, плавает и на деревьях растет?" М.Е.Салтыков-Щедрин


  1. dv0ich
    15.06.2025 15:06

    (по мотивам заглавной пикчи)

    Знакомьтесь, друзья: это Сишка-тян. Очень простая на вид и при должном подходе способна стать верным помощником, который выполняет кучу задач за день, но имеет некоторые особенности. Так, за её буфера ни в коем случае нельзя браться наощупь, это действие всегда требует чёткого визуального контроля, иначе велик шанс промахнуться мимо буферов, а от этого Сишка-тян моментально звереет и может проломить тебе голову каким-нибудь тяжёлым предметом или прострелить тебе ногу (или себе). Сишка-тян предпочитает утилитарные профессии, ей только дай покопаться в механике или электрике, но даже к её любимой работе её необходимо должным образом подготовить, иначе она всё разнесёт к чертям. Злые языки утверждают, что Сишка-тян - не кто иной, как Асм-кун, прошедший через пластическую операцию, но сама она это категорически отрицает.

    А это - Плюсо-тян. Близкая родственница Сишки-тян, но если та почти ничего за душой не имеет, то Плюсо-тян довольно состоятельна, владеет кучей имущества, как явно полезного, так и весьма странного. В целом, жизнь с ней куда проще и легче, чем с Сишкой-тян (можно браться за буфера наугад, не промахнёшься), но упаси боже тебя небрежно обращаться с её имуществом или с ней самой - тогда в ней просыпаются гены Сишки-тян и её поведение становится непредсказуемым вплоть до самых печальных последствий. По каким-то причинам имущество Плюсо-тян периодически увеличивается стараниями каких-то мутных дедов, но на вопросы почему и зачем это происходит Плюсо-тян отвечает загадочным молчанием.


    1. Mitai
      15.06.2025 15:06

      про Раст-куна не будет?


      1. lil_master Автор
        15.06.2025 15:06

        походу рыжих не любит