immich_native_core (Flutter package)
dart:ffi bindings to the immich_native_core Rust core. The native code is built
from source on every app build via a Dart build hook (Flutter native assets) — no
prebuilt binaries, no DynamicLibrary, no platform plugin glue.
Use it from immich/mobile
# mobile/pubspec.yaml
dependencies:
immich_native_core:
path: ../native/immich_native_core
dart pub get. The dart surface is exactly the ffigen output of the C header —
the current capabilities (EXIF rotate, 10-bit convert, thumbhash decode) are called
by the platform decode pipelines, not by dart, so there are no hand-written dart
wrappers. Add one only when a dart feature actually consumes a function.
No app-level Gradle/Podfile edits. hook/build.dart compiles the Rust crate and
Flutter bundles it as a code asset; the @Native bindings resolve against it.
Requirement: every machine that builds the app needs rustup
— the hook auto-installs the pinned toolchain + targets from the crate's
rust-toolchain.toml.
Layout
hook/build.dart— builds../crates/immich_core_ffivianative_toolchain_rust.lib/immich_native_core.dart— barrel; re-exports the generated bindings.lib/src/ffi/bindings.g.dart— ffigen@Nativeoutput (committed; do not edit).ffigen.yaml— ffi-native mode; asset-id must match the hook'sassetName.test/— host FFI roundtrip (flutter test); device runs viamobile/integration_test.
⚠ iOS App Extensions
Code assets are bundled into the app's Runner target. immich ships a Share Extension and a Widget Extension — if the core is ever called from one of those, verify the symbols resolve there (same family as the embed-into-Runner-only gotcha). Not an issue while only the main app calls it.
The Rust workspace, the codegen/build/test commands, and the "add a function" loop
live in ../README.md.