flutter_netbird

A Flutter FFI plugin for NetBird, providing secure mesh VPN connectivity for Flutter applications across Android, iOS, macOS, Windows, and Linux.

Features

  • 🌐 Cross-Platform: Android, iOS, macOS, Windows, and Linux support
  • 🔒 Secure Mesh VPN: Peer-to-peer encrypted connections using WireGuard
  • 📱 Userspace Networking: No root/admin privileges required on mobile
  • 🎯 Type-Safe API: Strongly typed Dart models for all operations
  • 📊 Real-time Status: Stream-based event and log monitoring
  • 🔧 Flexible Configuration: Comprehensive configuration options
  • 🌍 HTTP Proxy with DNS: HTTP requests through NetBird tunnel with in-process DNS resolution for Custom Zone domains
  • 🔗 Native Proxy & WebSockets: Built-in HTTP CONNECT proxy supports native dart:io and WebSocket transparent tunneling

Installation

Add this to your package's pubspec.yaml file:

dependencies:
  flutter_netbird:
    path: ../flutter_netbird  # Adjust path as needed

Quick Start

import 'package:flutter_netbird/flutter_netbird.dart';
import 'package:path_provider/path_provider.dart';

// 1. Get the SDK instance
final sdk = NetbirdSDK();

// 2. Set up event listeners
sdk.events.listen((event) {
  print('NetBird Event: ${event.type} - ${event.data}');
});

sdk.logs.listen((log) {
  print('${log.levelName}: ${log.message}');
});

// 3. Initialize with configuration
final docDir = await getApplicationDocumentsDirectory();
final config = NetbirdConfig(
  configPath: "${docDir.path}/netbird_config.json",
  statePath: "${docDir.path}/netbird_state.json",
  managementURL: "https://api.netbird.io",
  deviceName: "my-flutter-device",
  logLevel: "info",
);

sdk.initialize(config);

// 4. Connect with credentials
final auth = NetbirdAuth(setupKey: "YOUR-SETUP-KEY");
sdk.up(auth: auth);

// 5. Monitor status
Timer.periodic(Duration(seconds: 2), (timer) {
  final status = sdk.getFullStatus();
  print('Status: ${status.status}, IP: ${status.ip}');
});

// 6. Start Native Proxy (Optional: for WebSocket & dart:io support)
final proxyPort = await sdk.startProxy();
if (proxyPort > 0) {
  HttpOverrides.global = NetbirdHttpOverrides(proxyPort);
  // Now standard `WebSocketChannel` and `http.get` will securely route through NetBird!
}

API Reference

NetbirdSDK

The main SDK class providing all NetBird functionality.

Methods

initialize(NetbirdConfig config) → int

Initializes the NetBird SDK with configuration. Must be called before up().

Returns: 0 on success, non-zero on error.

final config = NetbirdConfig(
  configPath: "/path/to/config.json",
  statePath: "/path/to/state.json",
  managementURL: "https://api.netbird.io",
  deviceName: "my-device",
  logLevel: "info",
  setupKey: "optional-setup-key",  // Can be provided here or in up()
);

final result = sdk.initialize(config);
if (result != 0) {
  print('Initialization failed: $result');
}
up({NetbirdAuth? auth}) → int

Starts the NetBird connection.

Returns:

  • 0 on success
  • 1 on error
  • 2 if already running
final auth = NetbirdAuth(setupKey: "YOUR-SETUP-KEY");
final result = sdk.up(auth: auth);
down() → int

Stops the NetBird connection.

Returns: 0 on success, non-zero on error.

sdk.down();
getStatus() → String

Returns a simple status string.

Possible values: "Stopped", "Connecting", "Connected", "Disconnected", "Error"

final status = sdk.getStatus();
print('Current status: $status');
getFullStatus() → NetbirdFullStatus

Returns detailed status information as a typed object.

final status = sdk.getFullStatus();
print('Status: ${status.status}');
print('IP: ${status.ip}');
print('Peers: ${status.peersCount}');

for (var peer in status.peers) {
  print('Peer: ${peer.fqdn} (${peer.ip}) - ${peer.direct ? "Direct" : "Relayed"}');
}
setLogLevel(String level) → int

Changes the log level at runtime.

Valid levels: "trace", "debug", "info", "warn", "error"

sdk.setLogLevel("debug");
cleanup() → void

Cleans up all resources. Call this when shutting down the app.

@override
void dispose() {
  sdk.cleanup();
  super.dispose();
}

Properties

events → Stream<NetbirdEvent>

Stream of NetBird events (state changes, errors, etc.).

sdk.events.listen((event) {
  print('Event type: ${event.type}');
  print('Event data: ${event.data}');

  // Parse as JSON if needed
  final json = event.dataAsJson;
  if (json?['error'] != null) {
    print('Error occurred: ${json!['error']}');
  }
});
logs → Stream<NetbirdLog>

Stream of NetBird log messages.

sdk.logs.listen((log) {
  print('[${log.levelName}] ${log.message}');
});
isInitialized → bool

Whether the SDK has been initialized.

if (sdk.isInitialized) {
  // Safe to call up()
}

Configuration Classes

NetbirdConfig

Configuration for initializing NetBird.

NetbirdConfig({
  String? deviceName,           // Device identifier in the network
  String? managementURL,        // Management server URL
  String? configPath,           // Path to config file (for persistence)
  String? statePath,            // Path to state file
  String logLevel = 'info',     // Log level
  String? setupKey,             // Setup key (can be provided in up() instead)
  String? jwtToken,             // JWT token (alternative to setupKey)
  String? privateKey,           // Private key (alternative to setupKey)
  String? preSharedKey,         // WireGuard pre-shared key
  bool disableClientRoutes = false,  // Disable client routes
  bool blockInbound = false,    // Block inbound connections
  int? wireguardPort,           // WireGuard port (null = random)
})

Important: configPath is highly recommended for mobile apps to persist the device identity across restarts.

NetbirdAuth

Authentication credentials for connecting.

NetbirdAuth({
  String? setupKey,     // Setup key from NetBird management portal
  String? jwtToken,     // JWT token
  String? privateKey,   // Private key
})

Note: Only one authentication method should be provided.

Status Classes

NetbirdFullStatus

Comprehensive status information.

Properties:

  • String status - Connection status
  • String? error - Error message if any
  • Map<String, dynamic>? managementState - Management server state
  • Map<String, dynamic>? signalState - Signal server state
  • NetbirdPeerInfo? localPeer - Local peer information
  • int peersCount - Number of connected peers
  • List<NetbirdPeerInfo> peers - List of connected peers
  • int? forwardingRules - Number of forwarding rules
  • String? ip - Local NetBird IP address
  • String? publicKey - Public key
  • String? fqdn - Fully qualified domain name

NetbirdPeerInfo

Information about a peer.

Properties:

  • String? ip - Peer IP address
  • String? fqdn - Peer FQDN
  • String? connectionType - Connection type
  • bool? direct - Whether connection is direct (not relayed)
  • String? latency - Connection latency
  • int? bytesRx - Bytes received
  • int? bytesTx - Bytes transmitted

Memory Management

Critical: Callback Memory Safety ⚠️

This plugin has been designed with careful attention to memory management:

Callbacks (Events and Logs):

  • Safe: The Go side automatically frees memory after callbacks return
  • No action needed: Dart code should NOT call FreeString on callback data
  • Just read: Simply call toDartString() to copy the data to Dart

Return Values (Status Methods):

  • ⚠️ Dart must free: Strings returned from getStatus() and getFullStatus()
  • Handled internally: The SDK automatically calls FreeString - you don't need to worry about it
// ✅ CORRECT: Events are auto-freed
sdk.events.listen((event) {
  final data = event.data;  // Safe, memory auto-freed
});

// ✅ CORRECT: Status is auto-freed
final status = sdk.getFullStatus();  // Safe, SDK handles cleanup

Platform-Specific Notes

Android

  • Add the following to android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

iOS

  • Add the following to ios/Runner/Info.plist:
<key>NSLocalNetworkUsageDescription</key>
<string>NetBird needs access to local network for VPN connections</string>

macOS

  • Enable network entitlements in macos/Runner/DebugProfile.entitlements and Release.entitlements:
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>

Building Native Libraries

The native NetBird libraries are built from the netbird_core Go package:

cd ../netbird_core
./build_all.sh

This generates platform-specific binaries:

  • Android: libnetbird.so
  • iOS/macOS: libnetbird.xcframework / libnetbird.dylib
  • Windows: libnetbird.dll
  • Linux: libnetbird.so

Place these in the appropriate directories as specified in the plugin structure.

Example App

See the example/ directory for a complete Flutter application demonstrating:

  • ✅ Connection management
  • ✅ Real-time status monitoring
  • ✅ Peer list display
  • ✅ Log viewer
  • ✅ Settings configuration

Run the example:

cd example
flutter run

Troubleshooting

"Failed to load dynamic library"

Ensure the native library is properly included in your build:

  • Android: Check android/app/src/main/jniLibs/
  • iOS: Check Xcode framework embedding
  • macOS: Check that the library is in the bundle

DNS Resolution Fails ("server misbehaving")

If HTTP requests to custom domains fail with server misbehaving but IP access works:

  1. Verify the domain is configured as a DNS Zone (not Nameserver Group) in NetBird management
  2. Ensure the zone is Active and the A record is correctly configured
  3. The plugin resolves custom domains in-process from the management server's SyncResponse, bypassing netstack UDP DNS

Connection Fails

  1. Check your setup key is valid
  2. Verify the management URL is accessible
  3. Check logs for specific errors:
sdk.logs.listen((log) {
  if (log.level <= 2) {  // ERROR or FATAL
    print('ERROR: ${log.message}');
  }
});

Device Not Persisting

Make sure configPath is set to a writable location:

final docDir = await getApplicationDocumentsDirectory();
final config = NetbirdConfig(
  configPath: "${docDir.path}/netbird_config.json",  // Important!
  // ...
);

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is part of the NetBird ecosystem. See the main NetBird repository for licensing information.

Credits

Built on top of NetBird - Open-source mesh VPN platform.