flutter_pear

The full Pear peer-to-peer stack as a Dart-idiomatic Flutter plugin. Build serverless, end-to-end-encrypted P2P apps — discovery, encrypted connections, append-only logs, key/value stores, file drives, and multi-writer sync — without writing a line of Kotlin, Swift, or JavaScript.

Platforms: Android (stable, published) · iOS (SIMULATOR-VALIDATED — see iOS platform notes before shipping) · macOS/Linux/Windows desktop (new in 0.3.0 — a real Hyperswarm join, reaching connected, is confirmed on real hardware for all three; see Desktop dev setup and each platform's own notes for exactly what's covered). Requires Flutter SDK ≥ 3.24 (bundles Dart ≥ 3.5).

Status: pre-1.0, published on pub.dev (v0.3.0). The Bare Kit worklet is real (not a stand-in), and every data-structure wrapper (Corestore/Hypercore, Hyperbee, Hyperdrive, Autobase, blind pairing) is implemented and fake-tested end-to-end, with real-worklet validation on a real Android emulator, the iOS Simulator, and real macOS/Linux/Windows desktop hardware. Physical two-device mobile hardware validation is a documented follow-up, not a release gate. See the full repository README for the complete API coverage table.

Something stuck? Check Troubleshooting. Still stuck? Open an issue.

Unofficial. Not affiliated with Holepunch.

Install

flutter pub add flutter_pear

Native binaries and the P2P runtime resolve automatically — Gradle on Android, SwiftPM (with a CocoaPods compat path) on iOS. No manual NDK, ABI, or Podfile edits on either platform.

Pre-1.0: minor versions may break the API without notice. Pin an exact version once you depend on this for real.

Time to hello world (TTHW): P50 ≤ 5 minutes / P90 ≤ 10 minutes of active work, zero flutter_pear-specific build-wiring steps beyond one copy-paste Info.plist block on iOS — "hello world" means the first Android-to-iOS message, not just a successful build.

Quick start — chat over Hyperswarm

Two phones that share a topic find each other over the internet and exchange end-to-end-encrypted messages, no server:

import 'dart:convert';
import 'package:flutter_pear/flutter_pear.dart';

final pear = await Pear.start();

// A topic is a 32-byte rendezvous key both peers agree on out of band.
// unsafeTopicFromString is a GLOBAL, demo-only shortcut -- every device
// worldwide using the same string lands in the same room. Real apps
// derive a topic from a PearPairing invite instead.
final topic = PearCrypto.unsafeTopicFromString('my-secret-room');
final swarm = await pear.join(topic);

swarm.connections.listen((PearConnection conn) {
  conn.data.listen((bytes) {
    print('peer: ${utf8.decode(bytes)}');
  });
  conn.write(utf8.encode('hello from Flutter'));
});

// ... later
await swarm.leave();
await pear.dispose();

Expected output on each phone, once the other side's message arrives:

peer: hello from Flutter

Everything is Futures and Streams; keys are a PearKey value type with hex helpers (z-base-32 is planned, not yet implemented).

Enable iOS on an existing Android app

Already on flutter_pear 0.1.x, Android-only? Five steps get you to iOS:

  1. flutter create --platforms=ios . — plain Flutter, nothing flutter_pear-specific.
  2. flutter pub add flutter_pear:^0.2.0 — explicit, not a bare flutter pub upgrade: that command cannot cross the already-published ^0.0.1 caret. If you previously pinned flutter_pear_bare directly in your own pubspec.yaml, bump it the same way; if pub add reports a stale lock conflict, delete pubspec.lock and re-resolve.
  3. Paste this into ios/Runner/Info.plist (copied from iOS platform notes — see that page for why, and for the full symptom table if you skip this step):
    <key>NSLocalNetworkUsageDescription</key>
    <string>flutter_pear demos connect directly to your other devices over the local network to exchange chat messages and files.</string>
    
    Adjust the description to your own app's actual local-network use — Apple requires it be accurate, not necessarily this exact wording.
  4. flutter run on an iOS Simulator.
  5. Exchange your first message with an Android peer — same Pear.start()/join() code as above, no platform branching required for the happy path.

Received-file locations (if your app uses PearDrive/file transfer) differ by platform, matching what flutter_pear_example's own file-drop demo does: iOS saves into a Documents subtree (path_provider's getApplicationDocumentsDirectory()), visible in the Files app; Android saves into the app's private files directory (Context.getFilesDir()/received/), not independently visible — open or share it through your app's own affordance (a FileProvider content URI + ACTION_VIEW, in the example app's case). Neither location is where the worklet's own protocol storage lives — see Storage roots for that.

First-build download UX

Native binaries fetch once, then cache:

  • Android: downloads Bare Kit's native binaries, cached under each app's build/flutter_pear_bare/bare-kit/; delete that directory, or run flutter clean, to force a re-download.
  • iOS (SwiftPM, the default): downloads the repacked BareKit.xcframework (~107 MB), cached under ~/Library/Caches/org.swift.swiftpm; delete that directory, or run flutter clean, to force a re-download.
  • iOS (CocoaPods compat path): downloads the same artifact into ios/Pods/flutter_pear_bare/barekit_cache/<version>/; delete ios/Pods/ and re-run pod install to force a re-download.

Both platforms fetch from the same upstream holepunchto/bare-kit release; iOS's SwiftPM/CocoaPods binary-target mechanisms need a single ready-made BareKit.xcframework zip rather than Android's raw ~354 MB multi-platform prebuilds.zip, so flutter_pear republishes just that one framework, repacked and checksum-pinned — see barekit-pin.json for the exact pin chain.

Download-size disclosure (accept-and-disclose, standing decision — pub.dev downloads every dependency's committed files regardless of your target platform, flutter/flutter#130210): flutter_pear_bare's committed iOS addon .xcframeworks (bundled for every consumer, Android-only included) add ~21 MB to that package's own tracked content — measured directly (git ls-files + du), not a pub.dev-computed archive size. The example app's iOS build produces a Runner.app of ~59.7 MB (measured on the simulator archive) — this is an absolute number, not a delta: v0.1 had no iOS build at all to diff against.

Learn more

License

flutter_pear is MIT © 2026 Andrew Loable — see LICENSE.

It bundles the Pear stack (Bare Kit + Hyper* modules), which is MIT / Apache-2.0 — all permissive, no copyleft. Redistributed attributions ship in THIRD_PARTY_LICENSES (generated at build time). See LICENSING.md for the full dependency breakdown and obligations.

Libraries

flutter_pear
Dart-idiomatic Flutter API for the Pear P2P stack.