nitro 0.1.0 copy "nitro: ^0.1.0" to clipboard
nitro: ^0.1.0 copied to clipboard

High-performance Native Modules for Flutter (Nitro Modules equivalent). Runtime support for .native.dart spec generated bridges.

nitro ๐Ÿš€ #

Zero-overhead native bindings for Flutter. nitro is the runtime layer of the Nitrogen SDK โ€” it provides the base classes, annotations, and Dart-side runtime primitives that make type-safe, zero-copy FFI plugins possible on iOS and Android with zero method-channel overhead.

This package is the runtime dependency. Plugin authors add it to their pubspec.yaml. App developers pull it in transitively through any Nitrogen-powered plugin. The code generator lives in nitro_generator and the CLI in nitrogen_cli.


Why Nitro? #

Method Channel FFI (manual) Nitro
Overhead per call ~0.3 ms ~0 ms ~0 ms
Type safety stringly-typed manual generated, strict
Async support โœ… manual isolates โœ… generated
Streams โœ… slow manual SendPort โœ… zero-copy
Zero-copy buffers โŒ manual โœ… via @HybridStruct
Code to write lots enormous 3 files

Requirements #

Tool Minimum version
Flutter SDK 3.22.0+
Dart SDK 3.3.0+
Android NDK 26.1+ (r26b)
Kotlin 1.9.0+
iOS Deployment Target 13.0+
Swift 5.9+ (Xcode 15+)
Xcode 15.0+

Installation #

In your plugin's pubspec.yaml:

dependencies:
  nitro: ^0.1.0

Then run:

flutter pub get

Core concepts #

1. HybridObject โ€” the base class #

Every Nitrogen plugin's public API extends HybridObject. You never instantiate it directly; the code generator produces a _XxxImpl hidden class that does the real FFI work.

// lib/src/math.native.dart  โ† the ONLY file you write
import 'package:nitro/nitro.dart';

part 'math.g.dart';  // โ† generated

@NitroModule(ios: NativeImpl.swift, android: NativeImpl.kotlin)
abstract class Math extends HybridObject {
  static final Math instance = _MathImpl(NitroRuntime.loadLib('math'));

  // Synchronous FFI call โ€” executes in < 1 ยตs
  double add(double a, double b);

  // Async โ€” dispatched on a background isolate, returns to main isolate
  @nitroAsync
  Future<String> compute(String expression);
}

2. Annotations #

Annotation Where Effect
@NitroModule(ios:, android:) class Marks an abstract class as a Nitrogen module spec
@nitroAsync method Generated code dispatches call to a background isolate
@NitroStream(backpressure:) getter Streams native events to Dart via Dart_PostCObject
@HybridStruct(zeroCopy:) class Turns a Dart class into a C-struct with optional zero-copy fields
@HybridEnum(startValue:) enum Maps a Dart enum to a C int32 enum
@zeroCopy parameter Marks a Uint8List param as a raw native pointer (no copy)

3. @HybridStruct โ€” zero-copy data #

When a native method returns a large buffer (e.g. camera frame), mark the class with @HybridStruct and list the Uint8List fields that should be zero-copy:

@HybridStruct(zeroCopy: ['data'])
class CameraFrame {
  final Uint8List data;    // โ† mapped as Pointer<Uint8>, no copy
  final int width;
  final int height;
  final int stride;        // bytes per row โ€” auto-detected as byte-length
  final int timestampNs;

  CameraFrame(this.data, this.width, this.height, this.stride, this.timestampNs);
}

The generator produces a final class _CameraFrameFfi extends Struct with correct @Int64() annotations, and a toDart() extension that calls data.asTypedList(stride) โ€” zero allocations, zero copies.

4. @NitroStream โ€” native โ†’ Dart streaming #

@NitroModule(ios: NativeImpl.swift, android: NativeImpl.kotlin)
abstract class Camera extends HybridObject {
  static final Camera instance = _CameraImpl(NitroRuntime.loadLib('camera'));

  @NitroStream(backpressure: Backpressure.dropLatest)
  Stream<CameraFrame> get frames;  // 30fps camera frames, zero-copy
}

Backpressure options:

Value Behaviour
Backpressure.dropLatest Drop new item if Dart hasn't consumed yet โ€” best for sensors/camera
Backpressure.block Block the native thread until Dart consumes
Backpressure.bufferDrop Ring buffer โ€” oldest item dropped when full

Usage โ€” app developer side #

Once a Nitrogen plugin is added as a dependency, the API is completely type-safe Dart:

import 'package:my_camera/my_camera.dart';

// Sync call โ€” instant
final sum = Math.instance.add(3.14, 2.71);

// Async call โ€” runs on background isolate
final result = await Math.instance.compute('sqrt(144)');
print(result); // "12"

// Stream โ€” zero-copy frames at 30 fps
MyCamera.instance.frames.listen((frame) {
  // frame.data is a Uint8List backed by native hardware memory โ€” NO copy
  // frame.stride ร— frame.height = total bytes
  print('${frame.width}ร—${frame.height}  ${frame.data.length} bytes');
});

Usage โ€” plugin author side #

You write 3 files only:

1. lib/src/my_plugin.native.dart (Dart spec) #

import 'dart:typed_data';
import 'package:nitro/nitro.dart';

part 'my_plugin.g.dart';

@HybridStruct(zeroCopy: ['data'])
class ImageBuffer {
  final Uint8List data;
  final int stride;    // auto-detected as length source
  final int width;
  final int height;
  ImageBuffer(this.data, this.stride, this.width, this.height);
}

@NitroModule(ios: NativeImpl.swift, android: NativeImpl.kotlin)
abstract class MyPlugin extends HybridObject {
  static final MyPlugin instance = _MyPluginImpl(NitroRuntime.loadLib('my_plugin'));

  int add(int a, int b);

  @nitroAsync
  Future<String> processImage(String path);

  @NitroStream(backpressure: Backpressure.dropLatest)
  Stream<ImageBuffer> get frames;
}

Then run the generator (from your plugin root):

dart pub global run nitrogen_cli:nitrogen generate

2. android/.../MyPluginImpl.kt (Kotlin implementation) #

import nitro.myplugin_module.HybridMyPluginSpec
import nitro.myplugin_module.MyPluginJniBridge
import nitro.myplugin_module.ImageBuffer
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.delay

class MyPluginImpl : HybridMyPluginSpec {
    override fun add(a: Long, b: Long): Long = a + b

    override suspend fun processImage(path: String): String {
        delay(100) // simulate native work
        return "Processed: $path"
    }

    override val frames: Flow<ImageBuffer> = flow {
        val buf = java.nio.ByteBuffer.allocateDirect(1920 * 1080 * 4)
        while (true) {
            emit(ImageBuffer(buf, 1920L * 4L, 1920L, 1080L))
            delay(33) // ~30fps
        }
    }
}

class MyPluginPlugin : FlutterPlugin {
    override fun onAttachedToEngine(binding: FlutterPluginBinding) {
        MyPluginJniBridge.register(MyPluginImpl())
    }
    override fun onDetachedFromEngine(binding: FlutterPluginBinding) {}
}

3. ios/Classes/MyPluginImpl.swift (Swift implementation) #

import Flutter
import UIKit
import Combine

public class MyPluginImpl: NSObject, HybridMyPluginProtocol {
    public func add(a: Int64, b: Int64) -> Int64 { a + b }

    public func processImage(path: String) async throws -> String {
        try await Task.sleep(nanoseconds: 100_000_000)
        return "Processed: \(path)"
    }

    private let framesSubject = PassthroughSubject<ImageBuffer, Never>()
    public var frames: AnyPublisher<ImageBuffer, Never> {
        framesSubject.eraseToAnyPublisher()
    }

    override init() {
        super.init()
        let stride: Int64 = 1920 * 4
        let buf = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(stride * 1080))
        Timer.scheduledTimer(withTimeInterval: 1.0 / 30.0, repeats: true) { [weak self] _ in
            let frame = ImageBuffer(data: buf, stride: stride, width: 1920, height: 1080)
            self?.framesSubject.send(frame)
        }
    }
}

public class MyPluginPlugin: NSObject, FlutterPlugin {
    public static func register(with registrar: FlutterPluginRegistrar) {
        MyPluginRegistry.register(MyPluginImpl())
    }
}

Type mapping reference #

Dart type C type Kotlin type Swift type
int int64_t Long Int64
double double Double Double
bool int8_t Boolean Bool
String const char* String String
Uint8List uint8_t* ByteArray Data
Uint8List + zeroCopy uint8_t* java.nio.ByteBuffer UnsafeMutablePointer<UInt8>?
Future<T> N/A suspend fun async throws
Stream<T> SendPort reg. Flow<T> AnyPublisher<T, Never>

Repo structure #

nitro_ecosystem/
โ”œโ”€โ”€ packages/
โ”‚   โ”œโ”€โ”€ nitro/          โ† this package (runtime: base classes, annotations, runtime)
โ”‚   โ”œโ”€โ”€ nitro_generator/ โ† code generator (build_runner builder)
โ”‚   โ””โ”€โ”€ nitrogen_cli/   โ† CLI tool (nitrogen generate / init / doctor)
โ””โ”€โ”€ my_camera/          โ† example plugin built with Nitrogen
    โ”œโ”€โ”€ lib/src/
    โ”‚   โ”œโ”€โ”€ my_camera.native.dart   โ† spec (author-written)
    โ”‚   โ””โ”€โ”€ my_camera.g.dart        โ† generated FFI impl
    โ”œโ”€โ”€ android/                    โ† Kotlin implementation
    โ””โ”€โ”€ ios/                        โ† Swift implementation

License #

MIT

0
likes
150
points
0
downloads

Documentation

API reference

Publisher

verified publishershreeman.dev

Weekly Downloads

High-performance Native Modules for Flutter (Nitro Modules equivalent). Runtime support for .native.dart spec generated bridges.

Homepage
Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

ffi, flutter, plugin_platform_interface

More

Packages that depend on nitro

Packages that implement nitro