Open source · BSD-3-Clause · 4 packages on pub.dev

Flutter plugins for real hardware.

skio connects Flutter apps to USB serial devices, UART ports and USB cameras, on Android, macOS, Windows, Linux and in the browser. It calls native APIs directly with jnigen, dart:ffi and dart:js_interop, so there are no platform channels, and the logic lives in testable Dart.

$ flutter pub add skio_usb_serial
  • 4packages
  • 5platforms: Android, macOS, Windows, Linux, web
  • 0platform channels
  • 0telemetry

Illustration of the calls in the code sample below.

  • ESP32
  • STM32
  • Nordic nRF
  • Arduino
  • CP210x
  • CH340
  • FTDI
  • PL2303
  • CDC-ACM
  • Android USB OTG
  • macOS
  • Web Serial
  • UART
  • RS-232
  • RS-485
  • Android panels
  • Jetson
  • Raspberry Pi
  • Windows COM ports
  • UVC cameras
  • Endoscopes
  • Microscopes
  • USB webcams

Packages

One family, one way of doing things.

Each package covers one transport. They share permissions, errors, serial settings and device matching through skio_core.

Stable · v0.2

skio_usb_serial

Send and receive data with USB serial devices: Arduino and ESP32 boards, USB-to-serial adapters, instruments. Android over USB OTG, macOS apps, and Chrome or Edge with Web Serial. Windows and Linux are in progress.

  • Android
  • macOS
  • Web
  • ESP32
  • STM32
  • Arduino
  • CH340
  • FTDI
Beta · v0.1 · new

skio_uart

Open serial ports by path: the UART, RS-232 and RS-485 ports built into Android panels, Jetson and Raspberry Pi boards, Windows COM ports and USB adapters on Linux and macOS. Pure Dart FFI, nothing native to build, and it also runs in Dart command-line tools.

  • Android panels
  • Linux
  • Windows
  • macOS
  • RS-485
  • Jetson
  • Raspberry Pi
Stable · v0.2

skio_uvc_camera

USB cameras (endoscopes, microscopes, webcams) on Android and the web: live preview, JPEG photos, the camera's snapshot button, and your choice of resolution. macOS, Windows and Linux are in progress.

  • Android
  • Web
  • UVC
  • MJPEG
Stable · v0.2

skio_core

The shared building blocks: one permission API, one error family, device filters, serial settings and opt-in logging for every skio package.

  • Pure Dart
  • No Flutter dependency

How it works

From your widget to the wire, in Dart.

skio calls the platform APIs through generated bindings and dart:ffi. Buffering, framing and timeouts run in Dart, where you can test them. The diagram shows USB serial; skio_uart goes through dart:ffi to libc on Android, Linux and macOS and to kernel32 on Windows, and skio_uvc_camera takes the same path to UVCAndroid on Android and getUserMedia in the browser.

Your app
Flutter UI Widgets, state and your device's protocol.
skio · Dart
UsbSerialPort Ordered writes, timeouts, LineReader, one HardwareException type.
Android
jnigen → UsbManager Direct JNI calls, USB OTG.
macOS
dart:ffi → IOKit + termios No native code to compile.
Web
js_interop → Web Serial Chrome and Edge.
Hardware
Your device CP210x, CH340, FTDI or CDC-ACM over USB.
MethodChannelNo platform channel in between: no message codec, no stringly-typed method names. Typed calls go from Dart to the OS.

What it looks like

A small API that stays out of your way.

Open a USB adapter or a UART and exchange bytes, or open a camera and take a photo. Errors come from one sealed type, so a switch can handle every case.

  • DeviceFilter to match ports by vendor and product ID
  • SerialConfig for baud rate, data bits, parity, stop bits and flow control
  • dtr / rts set in the config, so ESP32 and Arduino boards don't reset on open
  • LineReader for lines and UTF-8 split across reads
  • UvcSize to choose the camera resolution from what it supports
  • SkioLog opt-in logs, down to every byte, for bug reports
final ports = await UsbSerialPort.list();
final port = await UsbSerialPort.open(ports.first,
    config: const SerialConfig(baudRate: 115200));

port.input.listen((bytes) => print(bytes));
await port.write(utf8.encode('hello\n'));
await port.close();

Platform support

Where each package runs.

All four packages are on pub.dev. The stable ones have been tested with real hardware on Android, macOS and the web; skio_uart is new and in beta.

PackageAndroidmacOSWindowsLinuxWeb
skio_usb_serialUSB serial adapters and boards stableUSB OTG, Android 7.0+ stableAny /dev/cu.* port in progressUse skio_uart today in progressUse skio_uart today stableWeb Serial, Chrome and Edge
skio_uartSerial ports by path betaBuilt-in UARTs, /dev/ttyS* beta/dev/cu.* betaAny COM port betaJetson, Raspberry Pi, PCs n/aNo serial ports in browsers
skio_uvc_cameraUSB (UVC) cameras stableUSB OTG, Android 7.0+ in progress in progress in progress stablegetUserMedia; no snapshot button
skio_coreShared types pure Dart pure Dart pure Dart pure Dart pure Dart

In progress

in progressWindowsUSB serial and USB cameras. Serial ports already work with skio_uart.
in progressLinuxUSB serial and USB cameras, starting with Ubuntu. Serial ports already work with skio_uart.
in progressmacOSUSB cameras. USB serial already works on macOS.
coming sooniPadUSB cameras on iPadOS 17 and later. Apple does not let iPhone or iPad apps use USB serial adapters.
Tested on:
  • vivo
  • OPPO
  • Samsung
  • POCO
  • Mac
  • Chrome on macOS
USB serial boards and chips:
  • ESP32
  • STM32
  • Nordic nRF
  • CP210x
  • CH340 / CH9102
  • FTDI
  • PL2303
  • CDC-ACM

Get started

Add it to your app in one line.

Each package's README on pub.dev explains every feature with examples.

  1. Add a package

    One command each. skio_core comes with them. skio_uart also works with dart pub add.

    $ flutter pub add skio_usb_serial
    $ flutter pub add skio_uart
    $ flutter pub add skio_uvc_camera
  2. Try the example apps

    A USB serial terminal, a UART terminal and a USB camera viewer. Each package has one in example/.

    $ git clone https://github.com/skio-flutter/skio
    $ cd skio/packages/skio_usb_serial/example
    $ flutter run
  3. On the web, ask from a tap

    Browsers only list serial ports the user has picked. Call request() from a button tap.

    FilledButton(
      onPressed: () => UsbSerialPort.request(),
      child: const Text('Choose port'),
    )
  4. On macOS, add the serial entitlement

    Sandboxed apps need it in both DebugProfile.entitlements and Release.entitlements.

    <key>com.apple.security.device.serial</key>
    <true/>
  5. On Linux, join dialout

    Your user needs access to serial ports for skio_uart. Log out and in again afterwards.

    $ sudo usermod -aG dialout $USER
  6. Test without hardware

    Swap the platform for a fake in your tests. Your own logic runs as plain Dart.

    import 'package:skio_usb_serial/platform_interface.dart';
    
    UsbSerialPlatform.instance = MyFakePlatform();

Principles

How skio is built.

A few rules every package follows.

  • Direct native bindingsjnigen, dart:ffi and js_interop. No MethodChannels, typed calls end to end.
  • Logic in DartQueues, framing, timeouts and retries are Dart code you can unit-test without a device.
  • Same API everywhereOne permission flow and one error type across every skio package.
  • Transports, not driversDevice protocols stay in your app, so any compatible device works.
  • No telemetryNo network access, no tracking. Your data stays where your app puts it.
  • Free and openBSD-3-Clause licence, free for personal and commercial use.

Build something that plugs in.

skio is open source and free to use. Stars, bug reports and pull requests all help it grow.