libtorrent_flutter 1.7.1 copy "libtorrent_flutter: ^1.7.1" to clipboard
libtorrent_flutter: ^1.7.1 copied to clipboard

Native libtorrent 2.0 bindings for Flutter with a built-in HTTP streaming server for instant torrent video playback. Supports Windows, Linux, macOS, iOS, and Android.

libtorrent_flutter #

The only Flutter package wrapping libtorrent 2.0 — the same C++ engine powering qBittorrent, Deluge, and Transmission. Add a magnet link, pick a file, get a stream URL. Works on Windows, Linux, macOS, iOS, and Android with zero setup — all native binaries are bundled.

dependencies:
  libtorrent_flutter: ^1.7.1

Why libtorrent? #

Every other Flutter torrent package uses either a Java wrapper (Android-only) or a pure-Dart implementation that can't compete on speed. libtorrent 2.0 gives you:

  • set_piece_deadline — tells the engine "I need this piece in 150ms", and it picks the fastest peer automatically. Far smarter than simple sequential download.
  • uTP support — connects to peers behind NAT that other clients can't reach.
  • On-demand streaming — focuses 100% of startup bandwidth on the first pieces. Container metadata (MP4 moov, MKV cues) is fetched reactively when the player requests it via HTTP range requests, cutting startup time to seconds.
  • DHT + PEX + LSD — finds peers without trackers.

Usage #

Initialize #

import 'package:libtorrent_flutter/libtorrent_flutter.dart';

// Call once, before anything else. 
// Saves to system temp dir by default — no permission setup needed on any platform.
await LibtorrentFlutter.init();

// Advanced options:
await LibtorrentFlutter.init(
  downloadLimit: 5 * 1024 * 1024,  // 5 MB/s cap
  uploadLimit:   1 * 1024 * 1024,  // 1 MB/s cap
  defaultSavePath: '/my/custom/path',
  fetchTrackers: true,  // auto-inject best public trackers (default: true)
  pollInterval: Duration(milliseconds: 500),
);

Add a torrent #

final engine = LibtorrentFlutter.instance;

// From a magnet link — save path defaults to system temp
final id = engine.addMagnet('magnet:?xt=urn:btih:...');

// From a magnet link with a custom save path
final id = engine.addMagnet('magnet:?xt=urn:btih:...', '/downloads');

// From a .torrent file
final id = engine.addTorrentFile('/path/to/file.torrent');

// Remove a torrent
engine.removeTorrent(id, deleteFiles: true);

// Pause / resume
engine.pauseTorrent(id);
engine.resumeTorrent(id);

Listen for status updates #

engine.torrentUpdates.listen((Map<int, TorrentInfo> torrents) {
  final t = torrents[id]!;
  print('${t.name}');          // torrent name
  print('${t.state.label}');   // "Downloading", "Seeding", etc.
  print('${t.progress}');      // 0.0 – 1.0
  print('${t.downloadRate}');  // bytes/sec
  print('${t.uploadRate}');    // bytes/sec
  print('${t.numPeers}');
  print('${t.numSeeds}');
  print('${t.hasMetadata}');   // true once file list is available
  print('${t.totalDone}');     // bytes downloaded
  print('${t.totalWanted}');   // bytes total
});

// Or get a snapshot right now
final Map<int, TorrentInfo> current = engine.torrents;

List files and start streaming #

// Wait for hasMetadata == true, then:
final files = engine.getFiles(id);  // List<FileInfo>

for (final f in files) {
  print('[${f.index}] ${f.name}');
  print('  size: ${f.size} bytes');
  print('  streamable: ${f.isStreamable}');
}

// Stream the largest streamable file (auto-selected)
final StreamInfo stream = engine.startStream(id);

// Stream a specific file by index
final StreamInfo stream = engine.startStream(id, fileIndex: 2);

// With a RAM cache limit — useful on mobile
// A 500MB sliding window: the engine evicts old pieces as you watch,
// keeping a 10% safety buffer behind playback so it never stutters.
final StreamInfo stream = engine.startStream(
  id,
  fileIndex: 2,
  maxCacheBytes: 500 * 1024 * 1024,  // 500 MB
);

print(stream.url);  // http://127.0.0.1:PORT/stream/...
// Hand this URL to any player: media_kit, video_player, VLC, mpv, etc.

Monitor stream status #

engine.streamUpdates.listen((Map<int, StreamInfo> streams) {
  for (final s in streams.values) {
    print('url: ${s.url}');
    print('ready: ${s.isReady}');       // true = player can connect
    print('buffer: ${s.bufferPct}%');   // 0–100 buffered ahead
    print('fileSize: ${s.fileSize}');
    print('readHead: ${s.readHead}');   // current byte position
  }
});

// Check if a specific torrent is being streamed
final bool active = engine.isStreaming(id);

// Get info for a specific stream
final StreamInfo? info = engine.getStreamInfo(streamId);

// Stop a specific stream
engine.stopStream(streamId);

// Stop all streams for a torrent
engine.stopAllStreamsForTorrent(torrentId);

Cleanup #

// Clean up one torrent: stops its streams, removes it, deletes the files
engine.disposeTorrent(id);  // returns false if already gone

// Clean up everything — perfect for your exit button
engine.disposeAll();

// Full shutdown — calls disposeAll(), destroys native session
await engine.dispose();

Speed limits #

engine.setDownloadLimit(2 * 1024 * 1024);  // 2 MB/s
engine.setUploadLimit(512 * 1024);          // 512 KB/s
engine.setDownloadLimit(0);                 // back to unlimited

Platform setup #

No changes needed on Windows, Linux, and Android — everything is bundled.

iOS — add the network entitlement. In your ios/Runner/Info.plist, ensure your app allows outgoing connections (this is allowed by default on iOS, but if you use a custom NetworkExtension or have restrictive NSAppTransportSecurity settings, make sure HTTP to localhost is permitted).

macOS — add the network entitlement to your macos/Runner/*.entitlements:

<key>com.apple.security.network.client</key>
<true/>

Android — add to AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />

Android background streaming — Android kills your app's process when it goes to the background unless you use a Foreground Service. When streaming, start one to keep the engine alive:

// Use flutter_foreground_task or a similar package
FlutterForegroundTask.startService(
  notificationTitle: 'Streaming',
  notificationText: 'Torrent engine running',
);

How it works #

A single C++ file (torrent_bridge.cpp) wraps libtorrent 2.0 and compiles to a native static/shared library on every platform. Dart talks to it via FFI — no platform channels, no Kotlin, no Swift.

When you call startStream():

  1. The engine sets piece deadlines on the first 6 pieces — piece 0 gets set_piece_deadline(0) triggering libtorrent's time-critical mode, which requests it from multiple peers simultaneously and cancels slow ones
  2. A tiny HTTP server starts on 127.0.0.1 on a random free port, running its own accept thread
  3. The server responds to byte-range requests, blocking via condition variables until each piece arrives — zero CPU polling
  4. When the player needs container metadata (moov atom, cues) from the end of the file, it sends a range request — the engine fetches those pieces on-demand with head/tail protection priorities
  5. A 5-level priority gradient (NOW=7, NEXT=6, READAHEAD=5, BACK=1, SKIP=0) keeps bandwidth focused on the playback window while maintaining a backward buffer for quick rewinds
  6. A configurable RAM piece cache serves repeated reads instantly, with safe-zone protection around the playhead and LRU eviction for distant pieces
  7. On seek, old piece deadlines are cleared immediately, new deadlines are set at the seek target with 200ms spacing, and the HTTP connection is preempted so the player's new request is accepted within milliseconds

The stream URL works with any player that handles HTTP range requests.


License #

GPL v3

7
likes
0
points
1.02k
downloads

Publisher

verified publisherplaytorrio.xyz

Weekly Downloads

Native libtorrent 2.0 bindings for Flutter with a built-in HTTP streaming server for instant torrent video playback. Supports Windows, Linux, macOS, iOS, and Android.

Repository (GitHub)
View/report issues

Topics

#torrent #streaming #libtorrent #ffi

License

unknown (license)

Dependencies

ffi, flutter

More

Packages that depend on libtorrent_flutter

Packages that implement libtorrent_flutter