Air Link Discovery
Zero-config LAN device discovery using UDP broadcast.
// Advertise this device
await DiscoveryService.advertise(name: 'My Phone', port: 8080);
// Discover other devices
DiscoveryService.discover().listen((peer) {
print('Found: ${peer.name} @ ${peer.ip}:${peer.port}');
});
// Cleanup
await DiscoveryService.stopAll();
No configuration, no IP scanning, no manual setup. Devices on the same network find each other automatically.
Quick Start
1. Add the dependency
dependencies:
air_link_discovery: ^0.2.0
2. Advertise your device
import 'package:air_link_discovery/air_link_discovery.dart';
await DiscoveryService.advertise(port: 8080, name: 'My Device');
The device broadcasts its name and port every 2 seconds over UDP. Other
devices running discover() will find it.
3. Discover peers
DiscoveryService.discover().listen((peer) {
print('${peer.name} at ${peer.ip}:${peer.port}');
// Connect using Air Link Core
final session = await Lan.connect(peer.ip, port: peer.port);
});
4. Stop when done
await DiscoveryService.stopAll();
Instance API
For running multiple discovery services independently (e.g., different apps on the same device):
final svc = DiscoveryService();
// Advertise
await svc.startAdvertise(name: 'Node A', port: 8081);
// Discover
svc.startDiscovery().listen((peer) {
print('Found via instance: ${peer.name}');
});
// Cleanup
await svc.shutdown();
Self-filtering
Each DiscoveryService instance generates a unique ID. By default,
discovery() filters out advertisements from the same instance
(filterSelf: true), so you never discover yourself.
API
Static methods (backward compatible)
| Method | Description |
|---|---|
advertise({name, port, metadata, identityFingerprint, discoveryPort, interval}) |
Start advertising |
discover({discoveryPort, filterSelf}) |
Start discovery, returns Stream<DiscoveredPeer> |
stopAll() |
Stop all discovery activity |
stopAdvertising() |
Stop advertising only |
stopDiscovering() |
Stop discovering only |
Instance methods
| Method | Description |
|---|---|
startAdvertise(...) |
Same as static advertise() |
startDiscovery(...) |
Same as static discover() |
stopAdvertise() |
Stop advertising |
stopDiscovery() |
Stop discovering |
shutdown() |
Stop all activity |
Configuration
| Parameter | Default | Description |
|---|---|---|
name |
Platform.localHostname |
Human-readable device name |
port |
(required) | The TCP port your server runs on |
metadata |
{} |
Additional key-value pairs to advertise |
identityFingerprint |
defaultFingerprint |
SHA-256 fingerprint of ECDSA identity key |
discoveryPort |
41234 |
UDP port for broadcast |
interval |
2 seconds | How often to broadcast |
filterSelf |
true |
Whether to filter own advertisements |
Global defaults
// Set once at app startup
DiscoveryService.defaultFingerprint = identity.fingerprint;
// All future adverts include it automatically
await DiscoveryService.advertise(port: 8080);
DiscoveredPeer
| Property | Description |
|---|---|
String name |
Device name |
String ip |
IP address |
int port |
TCP port |
Map<String, String> metadata |
Advertised metadata |
String? identityFingerprint |
ECDSA identity fingerprint (if provided) |
Security
Discovery sends plaintext UDP broadcasts. It is designed for discovery only — not for transmitting sensitive data.
- Use
identityFingerprintto verify the discovered peer matches the expected identity key when connecting viaair_link_secure - The broadcast includes an instance ID for self-filtering, but the ID is not authenticated — any device on the network can send advertisements
Platform Support
| Platform | Status |
|---|---|
| Android | ✅ (requires INTERNET permission) |
| iOS | ✅ (requires NSLocalNetworkUsageDescription) |
| macOS | ✅ |
| Windows | ✅ |
| Linux | ✅ |
Resource Limits
- The dedup set (prevents duplicate peer emissions) is capped at 1000 entries
- Old entries are automatically evicted on overflow
Module Integration
// Discovery + Secure: find peers with verified identity
final fp = identity.fingerprint;
await DiscoveryService.advertise(port: 8080, identityFingerprint: fp);
DiscoveryService.discover().listen((peer) {
if (peer.identityFingerprint != null) {
// Connect securely — the fingerprint matches the expected key
connectSecure(peer.ip, identity, port: peer.port);
}
});
// Discovery + Core: simple connect
DiscoveryService.discover().listen((peer) async {
final session = await Lan.connect(peer.ip, port: peer.port);
await session.peer.sendText('Hi! Found you via discovery.');
});
Requirements
- Dart SDK
>=3.0.0 <4.0.0 - No external dependencies
License
MIT