Skip to content
Blog

Connectivity Monitoring in Flutter with connectivity_plus

connectivity_plus 7.3.2 reports Wi-Fi, mobile, VPN and more on six platforms, but you still need a real reachability check before trusting it.

Published on • November 5, 2026

AI Assistant

“Offline mode” is the feature that quietly breaks in production: the banner never shows, the retry never fires, or the app claims it is online while the user sits on a hotel captive portal. connectivity_plus, now at version 7.3.2 and a Flutter Favorite from the verified fluttercommunity.dev publisher, tells you which network transports are available — on Android, iOS, macOS, web, Linux and Windows.

What it deliberately does not tell you is whether the internet actually works, and the readme says so in its own warning.

Checking once and listening for changes

checkConnectivity() returns a List<ConnectivityResult> (a device can have several transports active), and onConnectivityChanged streams those lists over time:

import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';

StreamSubscription<List<ConnectivityResult>>? _sub;

Future<void> checkNow() async {
  final results = await Connectivity().checkConnectivity();
  if (results.contains(ConnectivityResult.none)) {
    showOfflineBanner();
  } else if (results.hasConnectivity) {
    hideOfflineBanner(); // some transport is up
  }
}

void listen() {
  _sub = Connectivity().onConnectivityChanged.listen((results) {
    if (results.contains(ConnectivityResult.wifi)) {
      resumeDownloads();
    }
  });
}

@override
void dispose() {
  _sub?.cancel(); // always cancel the subscription
  super.dispose();
}

The ConnectivityResult values, per platform

The enum covers wifi, ethernet, mobile, vpn, bluetooth, satellite, other and none, but availability differs by OS. Android reports all of them; iOS and macOS have no separate vpn type (it comes back as other), and on Android, when both Wi-Fi and mobile data are on, only wifi is returned as the active network. Web is the sparsest: depending on browser support for the NetworkInformation API it degrades to navigator.onLine, which yields only wifi (online) or none — never an SSID or MAC address, which browsers refuse to expose for privacy reasons.

The “connected but no internet” problem

The readme is explicit: do not rely on connectivity status to decide whether a request will succeed, because connection type availability does not guarantee internet access — a Wi-Fi network behind a captive portal is the classic example. The fix is a second-layer probe against a real endpoint before you trust the connection:

Future<bool> hasRealInternet() async {
  try {
    final uri = Uri.parse('https://www.gstatic.com/generate_204');
    final response = await HttpClient()
        .getUrl(uri)
        .timeout(const Duration(seconds: 4));
    final reply = await response.close();
    return reply.statusCode == 204; // captive portals return 302/200 HTML
  } catch (_) {
    return false; // timeout, DNS failure, or socket error
  }
}

Run connectivity_plus to decide when to probe, and the probe to decide whether to send.

Platform setup is smaller than you think

Android connectivity reads are gated by ACCESS_NETWORK_STATE, and the plugin’s own AndroidManifest.xml already declares <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>, so manifest merging brings it into your app without edits. iOS and macOS need no Info.plist keys or entitlements — the implementation uses NWPathMonitor from iOS 12 / macOS 10.14 onward. Requirements for 7.x are Flutter 3.19+, Dart 3.3+, iOS 13+, macOS 10.15+, Java 17 and Android Gradle Plugin 8.12.1+.

Two runtime caveats from the readme: Android O+ stops delivering connectivity broadcasts in the background, so re-check when the app resumes; and NWPathMonitor can emit flaky sequences (a none immediately followed by wifi), so treat onConnectivityChanged as a hint to re-check, not as ground truth.

Offline-first UX patterns

Good offline design combines both layers: keep a cached ConnectivityResult in state, show a persistent but non-blocking banner when offline, queue mutations locally and flush them when the probe succeeds, and always retry requests with exponential backoff rather than assuming the first failure is fatal. Never block the whole UI on a connectivity callback — a false wifi must not crash a download, and a false none must not hide content the user already has.

Takeaway: Use connectivity_plus for cheap, cross-platform transport signals and stream updates for UI, but confirm real reachability with an HTTP probe and design every screen so a wrong answer degrades gracefully instead of breaking the flow.