# How FlClash's Proxy Manager Configures System-Wide Proxy Settings

> Learn how FlClash's proxy manager sets system-wide proxy settings by monitoring Riverpod state and executing platform commands. Discover the technical details.

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

---

**FlClash's proxy manager configures system-wide proxy settings by listening to Riverpod state changes in `ProxyState` and invoking platform-specific commands via the `Proxy` service to set OS-level HTTP/HTTPS proxies when `isStart` and `systemProxy` flags are both true.**

FlClash is a cross-platform GUI client for the Clash rule-based proxy engine. To route all system traffic through the local Clash instance, the application must bridge the gap between the Dart-based Flutter UI and the underlying operating system's network configuration. This is accomplished through a reactive proxy management layer that translates user preferences into native network stack modifications.

## Architecture Overview

The system-wide proxy configuration flow follows a reactive pattern from UI to kernel. When a user toggles the **System Proxy** option in the network settings, the change propagates through Riverpod state management to the `ProxyManager` class. This manager acts as the orchestration layer, deciding whether to activate or deactivate OS-level proxy settings based on two critical conditions: whether the Clash core is running (`isStart`) and whether the user has enabled system proxy exposure (`systemProxy`).

The actual platform-specific implementation resides in the `Proxy` service located in the `plugins/proxy` package. This service abstracts the differences between macOS, Linux, and Windows into a unified interface while delegating to native command-line tools or platform channels for the actual network stack manipulation.

## State Management and Listening

The `ProxyManager` widget registers a manual listener on the `proxyStateProvider` to react immediately to any changes in proxy configuration. This listener triggers the update scheduling mechanism whenever the state transitions.

In `lib/manager/proxy_manager.dart`, the listener is initialized as follows:

```dart
ref.listenManual(proxyStateProvider, (prev, next) {
  if (prev != next) {
    _scheduleUpdateProxy(next);
  }
}, fireImmediately: true);

```

This ensures that the system proxy configuration is applied immediately when the app starts (if conditions are met) and whenever the user modifies proxy settings through the UI. The `_scheduleUpdateProxy` method eventually delegates to `_updateProxy`, which contains the core decision logic for applying or removing system-wide settings.

## Decision Logic for Proxy Activation

The `_updateProxy` method evaluates the current `ProxyState` to determine whether to enable or disable the system proxy. It checks two boolean fields: **`isStart`** (indicating the Clash core is active) and **`systemProxy`** (indicating the user's preference to expose the proxy to the OS).

From `lib/manager/proxy_manager.dart`, lines 21-30:

```dart
final isStart = proxyState.isStart;
final systemProxy = proxyState.systemProxy;
if (isStart && systemProxy) {
  result = await proxy?.startProxy(port, proxyState.bassDomain);
} else {
  result = await proxy?.stopProxy();
}

```

Only when both conditions are true does the manager invoke `startProxy` with the current port and bypass domain list. If either condition fails—such as when the user stops the Clash core or disables the system proxy toggle—the manager immediately calls `stopProxy` to restore the system's original network configuration.

## Platform-Specific Implementations

The `Proxy` service in `plugins/proxy/lib/proxy.dart` implements distinct strategies for each supported operating system, ensuring compatibility with native network configuration mechanisms.

### macOS Implementation

On macOS, FlClash utilizes the **`networksetup`** command-line utility to configure system-wide proxies. The implementation iterates over all detected network services and applies HTTP and HTTPS proxy settings pointing to `127.0.0.1:<port>`.

```dart
final devices = await _getNetworkDeviceListWithMacos();
final commands = devices.expand(
  (dev) => _buildMacosStartCommands(dev, port, bypassDomain),
);
return _runCommands(commands);

```

This approach modifies the network settings for all active interfaces, ensuring that applications using the system's network stack route traffic through the local Clash instance.

### Linux Implementation

For Linux, the service detects the current desktop environment via the `XDG_CURRENT_DESKTOP` environment variable and selects the appropriate configuration backend. Supported environments include GNOME, MATE, and KDE Plasma.

The implementation generates specific commands for each backend:
- **GNOME**: Uses `gsettings` to modify `org.gnome.system.proxy` schemas
- **KDE**: Uses `dconf` or direct configuration file manipulation

```dart
final commands = await _resolveLinuxStartCommands(
  port, bypassDomain,
  desktop: Platform.environment['XDG_CURRENT_DESKTOP'],
  homeDir: homeDir,
);
return _runCommands(commands);

```

This desktop-aware approach ensures compatibility across different Linux distributions and windowing systems without requiring elevated privileges beyond standard user session capabilities.

### Windows Implementation

On Windows, the implementation delegates to a native platform plugin implemented in C++/Rust. The Dart layer calls the platform interface methods `ProxyPlatform.instance.startProxy` and `stopProxy`, which manipulate the Windows registry and WinINET settings to configure the system-wide proxy.

```dart
'windows' => await ProxyPlatform.instance.startProxy(port, bypassDomain),

```

This native implementation handles the complexity of Windows proxy configuration, including the system-level registry keys required for global traffic routing.

## Command Execution and Error Handling

All platform-specific implementations converge on the `_runCommands` method, which sequentially executes each `ProxyCommand` using Dart's `Process.run` API. The method validates exit codes to ensure successful application of settings.

From `plugins/proxy/lib/proxy.dart`, lines 33-46:

```dart
Future<bool> _runCommands(Iterable<ProxyCommand> commands) async {
  for (final command in commands) {
    final result = await Process.run(
      command.executable,
      command.arguments,
      runInShell: true,
    );
    if (result.exitCode != 0) {
      return false;
    }
  }
  return true;
}

```

If any command returns a non-zero exit code, the entire operation is reported as failed, allowing the UI to display appropriate error messages and revert state if necessary.

## Enabling System Proxy via UI

Users can toggle system-wide proxying through the Network configuration screen. The UI binds to the `networkSettingProvider` and updates the `systemProxy` field when the user interacts with the toggle switch.

In `lib/views/config/network.dart`:

```dart
final systemProxy = ref.watch(networkSettingProvider.select((s) => s.systemProxy));

SwitchListTile.adaptive(
  title: Text(appLocalizations.systemProxy),
  subtitle: Text(appLocalizations.systemProxyDesc),
  value: systemProxy,
  onChanged: (value) => ref
      .read(networkSettingProvider.notifier)
      .update((state) => state.copyWith(systemProxy: value)),
);

```

Programmatic control is also available through direct state mutation:

```dart
ref.read(proxyStateProvider.notifier).update((state) {
  return state.copyWith(
    isStart: true,
    systemProxy: true,
    port: 7890,
    bassDomain: [],
  );
});

```

This state change triggers the listener chain described above, resulting in immediate application of system-wide proxy settings.

## Summary

- **FlClash** uses a reactive architecture where `ProxyManager` listens to `proxyStateProvider` for changes in `isStart` and `systemProxy` flags.
- The decision to enable system-wide proxying occurs in `_updateProxy` within `lib/manager/proxy_manager.dart`, requiring both the Clash core to be running and the user to have enabled the system proxy option.
- Platform-specific implementations reside in `plugins/proxy/lib/proxy.dart`, using `networksetup` for macOS, desktop-environment-specific commands for Linux, and native platform channels for Windows.
- The `_runCommands` utility executes native CLI commands sequentially, validating exit codes to ensure successful configuration changes.
- UI controls in `lib/views/config/network.dart` allow users to toggle the `systemProxy` setting, which propagates through Riverpod to trigger OS-level network configuration.

## Frequently Asked Questions

### How does FlClash know when to apply system proxy settings?

FlClash monitors the `proxyStateProvider` Riverpod stream via `ref.listenManual` in the `ProxyManager` class. Whenever the `ProxyState` object changes—such as when the user toggles the system proxy switch or starts the Clash core—the listener triggers `_scheduleUpdateProxy`, which evaluates the current state and calls the appropriate platform-specific methods to apply or remove proxy settings.

### What happens if I enable system proxy but Clash isn't running?

The `ProxyManager` performs a safety check in its `_updateProxy` method, requiring both `isStart` (Clash core running) and `systemProxy` (user preference) to be true before calling `startProxy`. If the core is not running, the manager invokes `stopProxy` instead, ensuring that the system does not attempt to route traffic through a non-existent local proxy endpoint, which would result in connection failures.

### Which Linux desktop environments does FlClash support for system proxy configuration?

According to the source code in `plugins/proxy/lib/proxy.dart`, FlClash detects the `XDG_CURRENT_DESKTOP` environment variable to determine the active session and supports GNOME, MATE, and KDE Plasma. Each environment receives specific command generation logic—for example, `gsettings` for GNOME-based environments—ensuring compatibility across major Linux distributions without requiring manual configuration.

### How are proxy bypass domains handled in the system-wide configuration?

The `startProxy` method accepts a `bypassDomain` parameter (referenced as `bassDomain` in the state object) that specifies domains which should bypass the proxy. These domains are converted into platform-specific exclusion rules—for instance, passed to `networksetup` on macOS or incorporated into GNOME proxy exceptions on Linux—ensuring that traffic to specified domains routes directly rather than through the Clash tunnel.