flutter_network_doctor 0.2.0 copy "flutter_network_doctor: ^0.2.0" to clipboard
flutter_network_doctor: ^0.2.0 copied to clipboard

Production-grade network diagnostics for Flutter with transport, Wi-Fi, internet, DNS, TCP, TLS, IPv4, and IPv6 checks.

flutter_network_doctor #

CI pub package license: MIT

Production-grade network diagnostics for Flutter applications.

Instead of only answering “Wi-Fi or mobile?”, flutter_network_doctor builds a structured support report from operating-system connectivity state, real HTTP reachability, Wi-Fi/LAN metadata, DNS timing, TCP/TLS connection timing, and separate IPv4/IPv6 route checks.

Features #

  • Active transport detection: Wi-Fi, mobile, Ethernet, VPN, Bluetooth, satellite, and other
  • Real internet verification using a configurable quorum of HTTP endpoints
  • Wi-Fi/LAN metadata: SSID, BSSID, local IPv4/IPv6, subnet, broadcast, and gateway
  • DNS lookup, TCP connection, and TLS handshake timing on dart:io platforms
  • Separate IPv4 and IPv6 route checks
  • Native DNS server, route, interface, MTU, proxy, and path characteristics
  • Metered, expensive, constrained, validated, and captive-portal state where supported
  • Android 17 local-network permission readiness
  • Overall deadlines, cancellation, and progress callbacks
  • Balanced, strict, and internet-only health policies
  • Versioned JSON support reports with two levels of privacy redaction
  • Web and WebAssembly-safe behavior: unavailable socket probes return unsupported instead of throwing
  • No analytics, telemetry, or automatic permission prompts

Requirements #

  • Flutter 3.38.1 or later
  • Dart 3.10.0 or later, below Dart 4.0.0
  • Android API 21 or later
  • iOS 13.0 or later
  • macOS 10.15 or later
  • Java 17, Kotlin 2.2.0, Android Gradle Plugin 8.12.1 or later, and Gradle 8.13 or later for Android consumers

The effective platform minimums come from the package's current network_info_plus dependency.

Installation #

dependencies:
  flutter_network_doctor: ^0.2.0

Then install dependencies:

flutter pub get

Quick start #

import 'package:flutter_network_doctor/flutter_network_doctor.dart';

final doctor = FlutterNetworkDoctor();

try {
  final report = await doctor.diagnose();

  print(report.health);
  print(report.hasInternet);
  print(report.transports);
  print(report.wifi?.gateway);
  print(report.platform?.dnsServers);
  print(report.platform?.isMetered);

  final supportReport = report.toPrettyJson(
    redactWifiIdentifiers: true,
    redactNetworkAddresses: true,
  );
  print(supportReport);
} finally {
  doctor.dispose();
}

Call dispose() when the doctor is no longer needed. A caller-supplied http.Client remains owned by the caller and is not closed by the package.

Customize probes #

final report = await doctor.diagnose(
  config: NetworkDoctorConfig(
    timeout: const Duration(seconds: 3),
    overallTimeout: const Duration(seconds: 12),
    internetEndpoints: <Uri>[
      Uri.https('one.one.one.one'),
      Uri.https('icanhazip.com'),
    ],
    minimumInternetSuccesses: 1,
    dnsHost: 'example.com',
    tcpHost: '1.1.1.1',
    tcpPort: 443,
    tlsHost: 'cloudflare.com',
    tlsPort: 443,
    ipv4Host: '1.1.1.1',
    ipv6Host: '2606:4700:4700::1111',
    healthPolicy: NetworkHealthPolicy.balanced,
  ),
);

Each enabled probe is isolated. A failed DNS, socket, TLS, HTTP, or Wi-Fi metadata operation is represented in the report and does not abort the whole diagnostic run.

Progress and cancellation #

final cancellationToken = NetworkDoctorCancellationToken();

final diagnosis = doctor.diagnose(
  cancellationToken: cancellationToken,
  onProgress: (progress) {
    print(
      '${progress.stage.name}: '
      '${progress.completedProbes}/${progress.totalProbes}',
    );
  },
);

// Call from a cancel button or application lifecycle handler when needed.
cancellationToken.cancel();

try {
  final report = await diagnosis;
  print(report.totalDuration);
} on NetworkDoctorCancelledException {
  // The caller cancelled the run.
} on NetworkDoctorTimeoutException {
  // The complete run exceeded overallTimeout.
}

Health classification #

Value Meaning
healthy Internet reachability was verified and the selected health policy passed.
degraded Internet works, but a policy-relevant probe failed or a captive portal was reported.
localOnly A local transport exists, but the configured HTTP quorum did not verify internet access.
offline No usable transport was reported and internet access was not verified.
unknown Reserved for states that cannot be classified reliably.

Direct HTTP reachability takes precedence over a stale or unavailable operating-system transport result. This prevents a successful connection from being mislabeled as offline.

The default balanced policy treats DNS, TCP, and TLS as health signals while keeping missing IPv6 and redundant HTTP endpoint failures informational. Use strict when every supported probe must pass, or internetOnly when only the HTTP quorum should determine health.

Platform support #

The public API is usable on Android, iOS, macOS, Windows, Linux, and Web. Capability availability differs by platform:

Capability Android iOS macOS Windows Linux Web
Transport detection
HTTP reachability ✅*
Wi-Fi/LAN metadata
DNS timing
TCP timing
TLS timing
IPv4/IPv6 route check
Native path characteristics
Metered/expensive/constrained
Android 17 permission readiness

* Browser CORS and Content Security Policy rules apply to configured HTTP endpoints.

Web returns ProbeStatus.unsupported for raw DNS, TCP, TLS, IP-route, and native platform checks. Linux and Windows use the complete Dart probe set but currently return unsupported for the additional native platform snapshot. Unsupported capabilities never cause a crash or degrade balanced health.

Wi-Fi and local-network permissions #

This package never requests permissions on its own. The host application controls when and why a permission prompt is shown.

Android #

Apps performing HTTP or socket diagnostics normally declare:

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

SSID/BSSID access can require additional Wi-Fi or location-related permissions depending on the Android version and target SDK. Follow the current network_info_plus setup guidance for the metadata your application uses.

Android 17 enforces local-network protection for applications targeting SDK 37 or later. Direct LAN access then requires a suitable system-mediated picker or the ACCESS_LOCAL_NETWORK runtime permission. Version 0.2.0 reports notRequired, granted, or denied through report.platform?.localNetworkPermission, but deliberately does not declare or request this host-application permission.

Apple platforms #

On iOS, SSID/BSSID access requires the Access WiFi Information capability and one of Apple's permitted access conditions. Core Location authorization may also be required for the application's use case. macOS sandboxed applications must include the network entitlements appropriate to their inbound or outbound connections.

Captive portal detection #

captivePortalSuspected is a heuristic, not proof. It becomes true when the configured HTTP quorum fails and an endpoint responds with an unexpected redirect containing a location header. Captive portals that block requests, intercept TLS, or return a normal success page may not be detected.

Privacy #

The package contains no analytics or telemetry. Network probes necessarily contact the configured endpoints, so those servers can observe the application's public IP address like any normal network request. Use first-party endpoints when required by your privacy policy.

Before sharing a report, prefer:

final safeReport = report.toPrettyJson(
  redactWifiIdentifiers: true,
  redactNetworkAddresses: true,
);

redactWifiIdentifiers masks SSID and BSSID. redactNetworkAddresses additionally masks local IP, subnet, broadcast, gateway, DNS server, route, interface, proxy, private-DNS, and probe address fields.

Every JSON report includes schemaVersion and totalDurationMs so support systems can evolve parsers safely and track complete diagnostic latency.

Measurement notes #

  • DNS latency measures a host lookup through the operating system resolver; it is not a direct query to every configured DNS server.
  • TCP and TLS latency measure connection setup, not bandwidth or application request latency.
  • IPv4/IPv6 checks establish TCP connections to the configured literal addresses; they are not ICMP ping tests.
  • Timings can be influenced by DNS caches, connection policy, VPNs, proxies, firewalls, and platform scheduling.

Development #

flutter pub get
dart format --output=none --set-exit-if-changed .
flutter analyze
flutter test
flutter test --platform chrome
cd example
flutter test
flutter test integration_test/network_doctor_integration_test.dart -d macos
cd ..
dart pub publish --dry-run

The CI workflow builds all six targets and runs the integration smoke test on Android, iOS, Linux, macOS, Windows, and Web. The root performance regression tests also verify concurrent HTTP probe scheduling and bounded overall deadlines without relying on timing-sensitive external services.

See CONTRIBUTING.md for the contribution workflow.

Roadmap #

  • Native local-gateway reachability probe
  • Optional jitter and packet-loss sampling
  • Native Windows and Linux path characteristics
  • Reusable support/debug panel widget

Maintainer #

Malik

License #

MIT © 2026 Malik. See LICENSE.