flutter_local_device_discovery 2.0.0 copy "flutter_local_device_discovery: ^2.0.0" to clipboard
flutter_local_device_discovery: ^2.0.0 copied to clipboard

Local-network device and service discovery for Flutter using native mDNS/DNS-SD, Bonjour, SSDP, WS-Discovery, and secure UPnP metadata on Android, iOS, macOS, Windows, and Linux.

flutter_local_device_discovery #

pub package license Support on Ko-fi

Discover, resolve, classify, and monitor devices and services on the local network from Flutter. Version 2.0 brings native SSDP & WS-Discovery engines, native Linux D-Bus Avahi desktop support, network topology graph mapping, native Wi-Fi connection metadata, native ICMP pinging, permission querying, expanded 500+ OUI manufacturer resolution, and specialized smart home decoders (Matter, HomeKit HAP, Google Cast, AirPlay, Spotify Connect, Sonos, MQTT, Home Assistant, IPP).

Features #

  • Native Android NSD, Apple Network framework, Windows DNS-SD, and Linux D-Bus Avahi service discovery
  • Native SSDP and WS-Discovery multicast engines on Android, iOS, macOS, Windows, and Linux
  • Network topology graph builder (buildTopology()) with automatic gateway detection and hop-count analysis
  • Native Wi-Fi & network metadata inspection (getNetworkInfo()) for SSID, BSSID, RSSI, band, channel, speed, and gateway
  • Native ICMP reachability probing (probeIcmp())
  • Discovery permission checking and requesting (checkPermissions())
  • Offline IEEE OUI manufacturer resolution for 500+ vendor prefixes
  • Smart home & IoT protocol decoders (Matter/Thread, HomeKit HAP, Google Cast, AirPlay, Spotify, Sonos, MQTT, Home Assistant, IPP)
  • Modular DiscoveryProtocolAdapter API for integrating custom discovery engines
  • Multicast health diagnostics to detect router multicast drops / IGMP snooping
  • Secure, bounded UPnP device-description retrieval with anti-SSRF protections
  • Cross-protocol deduplication by UDN, hostname, address, and service identity
  • Full JSON serialization (toJson() / fromJson()) across all models

Platform support #

Platform Minimum Discovery support
Android API 21 Native NSD, native SSDP/UPnP, native WS-Discovery, Wi-Fi info, gateway detection, ARP cache
iOS 13.0 Network framework, native SSDP/UPnP, native WS-Discovery, Wi-Fi info, gateway detection
macOS 10.15 Network framework, native SSDP/UPnP, native WS-Discovery, CoreWLAN Wi-Fi info, gateway detection
Windows Flutter-supported versions Native DNS-SD/mDNS, native SSDP/UPnP, native WS-Discovery, WLAN API info, gateway detection
Linux Flutter-supported versions Native D-Bus Avahi mDNS/DNS-SD, native SSDP/WS-Discovery, NetworkManager info, /proc/net/route gateway
Web API compiles and reports unsupported; browsers cannot open the required multicast sockets

The package requires Dart 3.5 or later and Flutter 3.24 or later.

Installation #

Add the package to your application:

dependencies:
  flutter_local_device_discovery: ^2.0.0

Then run flutter pub get.

Snapshot discovery #

discover starts a bounded session, waits for the configured duration, returns its normalized snapshot, and releases the session resources.

import 'package:flutter_local_device_discovery/flutter_local_device_discovery.dart';

final discovery = FlutterLocalDeviceDiscovery();

final snapshot = await discovery.discover(
  const LocalDiscoveryRequest(
    duration: Duration(seconds: 8),
    protocols: {
      LocalDiscoveryProtocol.mdns,
      LocalDiscoveryProtocol.dnsSd,
      LocalDiscoveryProtocol.ssdp,
      LocalDiscoveryProtocol.upnp,
    },
    serviceTypes: {
      '_http._tcp',
      '_ipp._tcp',
      '_googlecast._tcp',
    },
    ssdpSearchTargets: {'ssdp:all'},
    fetchUpnpDescriptions: true,
  ),
);

for (final device in snapshot.devices) {
  print('${device.displayName}: ${device.addresses}');
}

UPnP metadata retrieval is opt-in. Set fetchUpnpDescriptions to true and include either ssdp or upnp in protocols.

Continuous discovery #

Keep the event subscription and session, then cancel and stop both when the owning component is disposed.

final session = await discovery.start(
  const LocalDiscoveryRequest(
    mode: LocalDiscoveryMode.continuous,
    protocols: {
      LocalDiscoveryProtocol.mdns,
      LocalDiscoveryProtocol.dnsSd,
      LocalDiscoveryProtocol.ssdp,
    },
    serviceTypes: {'_http._tcp'},
    ssdpSearchTargets: {'ssdp:all'},
  ),
);

final subscription = session.events.listen((event) {
  switch (event) {
    case LocalDeviceAdded(:final device):
      print('Added: ${device.displayName}');
    case LocalDeviceUpdated(:final device):
      print('Updated: ${device.displayName}');
    case LocalDeviceRemoved(:final device):
      print('Removed: ${device.displayName}');
    case LocalDiscoveryWarning(:final message):
      print('Warning: $message');
    case LocalDiscoveryFailure(:final error):
      print('Failure: $error');
    case _:
      break;
  }
});

// When discovery is no longer needed:
await subscription.cancel();
await session.stop();

Sessions also support pause(), resume(), and snapshot().

Capabilities and readiness #

Check support before presenting protocol-specific controls, then check whether a concrete request can start:

final capabilities = await discovery.getCapabilities();
print(capabilities.supportedProtocols);

final request = const LocalDiscoveryRequest(
  protocols: {LocalDiscoveryProtocol.ssdp},
  ssdpSearchTargets: {'ssdp:all'},
);
final readiness = await discovery.checkReadiness(request);

if (!readiness.canStart) {
  print('Requirements: ${readiness.requirements}');
}

Classification evidence and UPnP identity #

Classification is an inference from observable service and device metadata. Applications can inspect the supporting evidence instead of treating a classification as authoritative.

for (final device in snapshot.devices) {
  print('Type: ${device.type}');
  print('UPnP UDN: ${device.identity.upnpUdn}');

  for (final evidence in device.capabilityEvidence) {
    print('${evidence.capability}: ${evidence.source}');
  }
}

Platform configuration #

Android #

The Android implementation declares these permissions and they are merged into the consuming application manifest:

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

iOS #

Add a purpose string and every Bonjour service type your application browses to ios/Runner/Info.plist:

<key>NSLocalNetworkUsageDescription</key>
<string>This app discovers devices and services available on your local network.</string>
<key>NSBonjourServices</key>
<array>
    <string>_http._tcp</string>
    <string>_ipp._tcp</string>
</array>

Only declare service types the application actually uses.

macOS #

Add the same NSLocalNetworkUsageDescription and NSBonjourServices entries to macos/Runner/Info.plist. Sandboxed applications also need client and server networking in both debug/profile and release entitlements:

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

Enabling Local Network Access (macOS & iOS)

If you encounter Network.NWError error -65555 - NoAuth or no devices appear, verify that Local Network permissions are enabled:

On macOS:

  1. Open System Settings ( > System Settings).
  2. Click Privacy & Security in the sidebar.
  3. Scroll down and click Local Network.
  4. Find device_discovery (or Runner / your terminal or IDE if running in debug mode) and toggle the switch to ON (Enabled).
  5. Re-open or re-run the app.

On iOS:

  1. Open the Settings app on the iPhone/iPad.
  2. Scroll down to Device Discovery (or Privacy & Security > Local Network).
  3. Ensure the Local Network switch is turned ON.

Windows #

Windows Firewall or enterprise policy can filter multicast UDP. Test on the intended network profile; the plugin does not create firewall exceptions.

UPnP metadata security #

UPnP descriptions are untrusted network input. The default MetadataSecurityPolicy:

  • permits private and link-local targets only
  • blocks public and loopback targets
  • validates and pins resolved connection addresses
  • validates every redirect target
  • limits redirects, response size, request time, and XML depth
  • rejects XML document types and entity declarations

Relax allowExternalAddresses or allowLoopbackAddresses only when the application explicitly trusts those targets.

Important behavior #

  • Discovery is not authentication, pairing, or a guaranteed network inventory.
  • Guest Wi-Fi, client isolation, VPNs, firewalls, and enterprise multicast policy can hide devices.
  • A missing response does not prove that a device is offline.
  • Service and device metadata can be malformed or intentionally deceptive.
  • Neighbor-table inspection, reachability probing, and safe port probing are fully implemented and supported in v1.0.0.

Example #

The included example application is a v0.2 review console with protocol controls, live device/service events, UPnP metadata, capability evidence, readiness, and diagnostics.

Additional documentation #

License #

MIT. See LICENSE.