rx_connectivity_checker 2.0.0
rx_connectivity_checker: ^2.0.0 copied to clipboard
A robust, reactive Dart/Flutter package for monitoring network connectivity.
rx_connectivity_checker #
A robust, reactive, and high-performance federated Flutter library for monitoring network connectivity.
Beyond monitoring local connection states, this library verifies end-to-end internet access through active network probes. Validation is triggered by three distinct events:
- Platform Change Events — when the system reports that the network is gone (e.g. Wi-Fi and mobile data turned off),
offlineis emitted instantly, without a probe. When a network becomes available or changes, a probe fires immediately; if it fails, follow-up probes run on a short back-off (250 ms, 500 ms, 1 s, 2 s, 4 s by default, configurable viarecheckDelays) until the network is reachable — a link that has just come up often cannot carry traffic yet. - Periodic Polling — automated checks run every
checkFrequencywhile online, to catch "silent" losses (e.g. a router losing its uplink while Wi-Fi remains connected), and everyofflineCheckFrequency(5 s by default) while not online, to catch reconnections the platform does not report (e.g. the iOS Simulator). Polling pauses while the app is hidden and resumes with an immediate check when it becomes visible again. - Manual Triggers —
checkConnectivity()runs a fresh probe right away, always emits its result and restarts the periodic timer.
Features #
- Federated Architecture — native implementations for Android, iOS, macOS, Windows, and Linux.
- True Reachability — distinguishes between "connected to a network" and "having internet access."
- Performance Optimised — periodic probes are skipped while another probe is in flight, and results made stale by a newer network change are discarded.
- Modern Web Support — WASM-ready using
package:webanddart:js_interopwith intelligent CORS-aware probes. - Energy Efficient — leverages native system observers (
NWPathMonitor,ConnectivityManager,DBus, NLM) to trigger checks only when necessary. - Shared, Restartable Stream — one checker can back the whole app session or only some screens: monitoring starts with the first listener, stops with the last, restarts when a screen listens again, and new listeners immediately receive the last known status.
- Zombie-Connection Detection — periodic background checks catch cases where a device is locally connected but has no actual internet access.
- Manual Check Support —
checkConnectivity()always runs a fresh, unthrottled check, always emits its result and restarts the periodic timer. - Testing Friendly — full dependency injection via
IHttpClientandReachabilityValidatoreliminates the need for real network access in tests.
Installation #
Requires Flutter 3.44 or later. Add rx_connectivity_checker to your pubspec.yaml:
dependencies:
rx_connectivity_checker: ^2.0.0
Then run:
flutter pub get
Usage #
Basic setup #
Initialise the checker with a reliable endpoint. Google's generate_204 is the default — it is fast, lightweight, and returns no body.
final connectivityChecker = ConnectivityChecker(
url: 'https://connectivitycheck.gstatic.com/generate_204',
checkFrequency: const Duration(seconds: 30),
checkSlowConnection: true, // successful responses slower than slowConnectionThreshold report `slow`
slowConnectionThreshold: const Duration(seconds: 3),
);
Reactive UI updates #
Use connectivityStream to rebuild your UI whenever the network state changes. A new listener immediately receives the last known status (ConnectivityStatus.unknown before the first check completes); after that the stream emits when the status changes, and for every checkConnectivity() result.
StreamBuilder<ConnectivityStatus>(
stream: connectivityChecker.connectivityStream,
initialData: ConnectivityStatus.unknown,
builder: (context, snapshot) {
final status = snapshot.data ?? ConnectivityStatus.unknown;
return Column(
children: [
Icon(
status == ConnectivityStatus.online ? Icons.wifi : Icons.wifi_off,
color: status == ConnectivityStatus.online ? Colors.green : Colors.red,
),
Text('Status: ${status.name}'),
],
);
},
)
Sharing one checker across the app or across screens #
Create a single ConnectivityChecker (e.g. in your dependency-injection setup) and listen to the same connectivityStream wherever you need it:
- Whole app session — keep one long-lived listener (e.g. at the app root). Monitoring runs for the entire session, pausing periodic checks while the app is hidden.
- Only some screens — listen from those screens only. Monitoring stops when the last screen stops listening and restarts, with the last known status replayed, when another screen listens.
Manual one-off checks #
For critical actions (like submitting a form), perform an immediate check. Every call runs a fresh probe — it never reuses an older in-flight one — completes within timeout, emits its result on connectivityStream (even when the status is unchanged) and restarts the periodic timer.
final status = await connectivityChecker.checkConnectivity();
if (status == ConnectivityStatus.online) {
await apiService.uploadData();
}
Dependency injection (testing) #
ConnectivityChecker accepts an IHttpClient so you can swap in a mock during tests:
class MockHttpClient implements IHttpClient {
@override
Future<http.Response> get(Uri url, {Map<String, String>? headers}) async {
return http.Response('', 204);
}
}
final checker = ConnectivityChecker(client: MockHttpClient());
ConnectivityStatus #
| Value | Meaning |
|---|---|
online |
Remote endpoint responded with a 2xx status code. |
offline |
Endpoint unreachable, no response within timeout, or a non-2xx response. |
slow |
A 2xx response that took longer than slowConnectionThreshold, with checkSlowConnection set to true. |
unknown |
Initial state before the first check completes. |
Platform-specific behaviour #
Slow vs. offline: a request that gets no response within timeout is always offline, on every platform — a network without internet access (for example an emulator whose Wi-Fi has no upstream) makes requests hang rather than fail. slow is reserved for connections that work but respond slower than slowConnectionThreshold; keep the threshold below timeout, otherwise slow can never occur.
Web limitation: The web implementation uses the browser's native fetch() API. IHttpClient injection and custom headers are not supported on web — passing either has no effect. Use a native platform build if you need header or client injection.
Platform support & native mechanisms #
| Platform | Mechanism | Details |
|---|---|---|
| Android | ConnectivityManager |
Follows the system default network via registerDefaultNetworkCallback. |
| iOS / macOS | NWPathMonitor |
Native Swift implementation with event deduplication. |
| Linux | D-Bus / NetworkManager |
Subscribes to StateChanged signals on the system bus. |
| Windows | INetworkListManager |
C++ COM interop for high-performance desktop tracking. |
| Web | fetch API |
Uses cors mode; a CORS rejection is treated as reachable since the server responded. |
Platform setup #
Android #
Add the following permissions to android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
iOS / macOS #
The plugin supports both Swift Package Manager and CocoaPods — no extra setup is needed for either.
No additional permissions are required for iOS.
For macOS, enable the network client entitlement in both macos/Runner/DebugProfile.entitlements and macos/Runner/Release.entitlements:
<key>com.apple.security.network.client</key>
<true/>
Windows #
The Windows implementation uses COM interop. No manifest changes are required for standard Win32 apps. For MSIX / packaged apps, declare the internetClient capability in Package.appxmanifest:
<Capability Name="internetClient" />
Ensure your build environment targets C++20 or later (required by the plugin's CMakeLists.txt).
Linux #
The Linux implementation communicates with NetworkManager via D-Bus. Most modern distributions (Ubuntu, Fedora, etc.) ship NetworkManager by default — no additional setup is required.
Web (CORS) #
The web implementation uses mode: cors. If the fetch succeeds the endpoint is reachable. If the server responds with a CORS rejection (e.g. TypeError), the browser still contacted the server — the validator treats this as online. A true network failure (DNS error, no route) sets navigator.onLine to false and is correctly reported as offline.
Example #
A complete, runnable example is in the example/ directory. It demonstrates:
- implementing a global connectivity listener
- handling
ConnectivityStatus.slowstates - using manual triggers before form submissions
cd example
flutter run
Contributing #
Contributions are welcome. For bugs or feature requests, please file them on the GitHub Issue Tracker.