# How FlClash's Connectivity Manager Detects and Responds to Network Changes

> Discover how FlClash's Connectivity Manager detects and responds to network changes using connectivity_plus and Riverpod for real-time UI updates. Learn about Wi-Fi SSID fetching.

- Repository: [chen08209/FlClash](https://github.com/chen08209/FlClash)
- Tags: internals
- Published: 2026-05-31

---

**FlClash uses a stateful widget called `ConnectivityManager` that leverages the `connectivity_plus` plugin to monitor platform network events, automatically fetches the current Wi-Fi SSID via `WifiSsidManager`, and propagates changes through Riverpod providers to update the UI in real time.**

The connectivity manager in FlClash centralizes network detection logic within a single widget that can be placed at the root of the application tree. This architecture ensures that any descendant widget can react to connectivity changes without implementing individual listeners, while the manager handles platform-specific details like SSID retrieval and provider updates.

## Architecture of the Connectivity Manager

The manager is implemented as a `StatefulWidget` in `lib/manager/connectivity_manager.dart` that accepts a child widget and an optional callback. Rather than rendering UI itself, it acts as an invisible wrapper that maintains a subscription to the operating system's network state broadcasts and updates global application state accordingly.

This pattern decouples network detection from business logic, allowing the rest of the FlClash codebase to simply watch `currentSSIDProvider` for Wi-Fi name changes or use the callback for custom connectivity handling.

## Step-by-Step Network Detection Flow

### Initializing the Platform Listener

When the widget mounts, `initState` creates a subscription to `Connectivity().onConnectivityChanged`, a broadcast stream provided by the `connectivity_plus` package. This stream emits a `List<ConnectivityResult>` every time the OS reports a network transition—such as switching from Wi-Fi to mobile data or losing connection entirely.

```dart
// lib/manager/connectivity_manager.dart
@override
void initState() {
  super.initState();
  subscription = Connectivity().onConnectivityChanged.listen((results) {
    // Handle results...
  });
}

```

### Wi-Fi Detection and SSID Retrieval

When the emitted list contains `ConnectivityResult.wifi`, the manager queries `WifiSsidManager.instance.getSsid()` to fetch the current network name. This asynchronous operation updates the `currentSSIDProvider` through `globalState.container`, making the SSID available to any watching widget.

```dart
// From lib/manager/connectivity_manager.dart
if (results.contains(ConnectivityResult.wifi)) {
  WifiSsidManager.instance.getSsid().then((ssid) {
    globalState.container.read(currentSSIDProvider.notifier).value = ssid;
  });
}

```

### Handling Non-Wi-Fi States

If the connectivity results do not include Wi-Fi—indicating mobile data, Ethernet, or no connection—the manager explicitly clears the provider by setting it to `null`. This ensures that dependent UI elements correctly display offline status or switch to cellular-specific proxy rules.

```dart
// lib/manager/connectivity_manager.dart
else {
  globalState.container.read(currentSSIDProvider.notifier).value = null;
}

```

### Support for External Callbacks

After internal state updates, the manager forwards the raw `results` list to an optional `onConnectivityChanged` callback if provided. This allows consumers to implement additional logic—such as logging, analytics, or offline banners—without modifying the manager's source code.

```dart
// lib/manager/connectivity_manager.dart
if (widget.onConnectivityChanged != null) {
  widget.onConnectivityChanged!(results);
}

```

### Resource Cleanup

To prevent memory leaks, the subscription is cancelled in the `dispose` method when the widget is removed from the tree. This is critical for long-running applications where the connectivity listener might otherwise persist indefinitely.

```dart
// lib/manager/connectivity_manager.dart
@override
void dispose() {
  subscription.cancel();
  super.dispose();
}

```

## State Management and Provider Structure

The SSID value is stored in `currentSSIDProvider`, a Riverpod provider generated in `lib/providers/generated/app.g.dart`. By writing to `globalState.container.read(currentSSIDProvider.notifier)`, the manager ensures that any widget watching this provider automatically rebuilds when the network changes.

This provider-based approach eliminates the need for `setState` or callback drilling, allowing deeply nested widgets to react to connectivity changes with minimal boilerplate.

## Implementation Examples

### Wrapping the Application Root

Place `ConnectivityManager` at the top of the widget tree to ensure global network monitoring. The optional callback can be used for application-wide logging or analytics.

```dart
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ConnectivityManager(
      onConnectivityChanged: (results) {
        debugPrint('Network changed: $results');
      },
      child: MaterialApp(
        home: HomeScreen(),
      ),
    );
  }
}

```

### Consuming the Wi-Fi SSID

Widgets can read the current network name using Riverpod's `ConsumerWidget`. The UI updates automatically when the manager detects a new SSID or connection loss.

```dart
class WifiInfoTile extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final ssid = ref.watch(currentSSIDProvider);
    return ListTile(
      leading: const Icon(Icons.wifi),
      title: Text(ssid ?? 'Not connected to Wi-Fi'),
    );
  }
}

```

### Reacting to Raw Connectivity Changes

For logic that depends on connection type rather than SSID, use the callback to implement custom behavior when the device goes offline or switches networks.

```dart
ConnectivityManager(
  onConnectivityChanged: (results) {
    if (results.contains(ConnectivityResult.none)) {
      // Trigger offline mode, pause sync, or show banner
    }
  },
  child: Scaffold(
    body: Dashboard(),
  ),
);

```

## Key Source Files

- **`lib/manager/connectivity_manager.dart`** – Implements the `StatefulWidget` that manages the `connectivity_plus` subscription, SSID updates, and optional callbacks.
- **`lib/providers/generated/app.g.dart`** – Contains the Riverpod `currentSSIDProvider` definition used for state propagation.
- **`lib/state.dart`** – Provides `globalState.container`, the interface used by the manager to write provider values.

## Summary

- The FlClash connectivity manager is a wrapper widget in `lib/manager/connectivity_manager.dart` that listens to `Connectivity().onConnectivityChanged` from the `connectivity_plus` plugin.
- It automatically queries `WifiSsidManager.instance.getSsid()` when Wi-Fi is detected and updates `currentSSIDProvider` via `globalState.container`.
- Non-Wi-Fi states clear the provider to `null`, ensuring accurate offline detection.
- An optional `onConnectivityChanged` callback allows custom handling of raw connectivity results without modifying the manager's core logic.
- The subscription is cancelled in `dispose()` to prevent memory leaks.
- Riverpod providers ensure that any widget in the tree can react to network changes with minimal overhead.

## Frequently Asked Questions

### How does FlClash distinguish between Wi-Fi and mobile data connections?

The manager checks if the `List<ConnectivityResult>` emitted by the platform contains `ConnectivityResult.wifi`. If present, it treats the connection as Wi-Fi and attempts to fetch the SSID; otherwise, it sets the provider to `null`, indicating either mobile data, Ethernet, or no connection.

### Why does the connectivity manager use Riverpod instead of setState?

Using `currentSSIDProvider` from Riverpod allows the manager to decouple network detection from UI rendering. Any widget in the application can watch the provider and rebuild automatically when the SSID changes, without the manager needing to know which widgets depend on the state.

### Where is the Wi-Fi SSID actually stored in FlClash?

The SSID is stored in `currentSSIDProvider`, which is defined in `lib/providers/generated/app.g.dart`. The manager writes to this provider using `globalState.container.read(currentSSIDProvider.notifier).value = ssid`.

### Can I use the connectivity manager without displaying the Wi-Fi name?

Yes. The manager supports an `onConnectivityChanged` callback that receives the raw `List<ConnectivityResult>` for every network change. You can implement this callback to handle connectivity logic—such as offline detection—while ignoring the SSID provider entirely.