# CoreController Singleton in FlClash: The Bridge Between Flutter and Go

> Discover how the CoreController singleton in FlClash bridges Flutter UI and the Go Clash core. It handles platform-specific communication and offers a unified Dart API for core operations.

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

---

**The `CoreController` singleton serves as the sole communication bridge between FlClash's Flutter UI and the Clash Meta core written in Go, instantiating platform-specific handlers (FFI for Android, sockets for Desktop) and exposing a unified Dart API for all core operations.**

FlClash is a cross-platform proxy client that combines a Flutter frontend with the Clash Meta core written in Go. The `CoreController` singleton manages all Flutter-Go communication, ensuring the mobile and desktop applications interact with the underlying Go engine through a consistent interface regardless of whether the app runs on Android or Desktop operating systems.

## Singleton Pattern and Global Access

The `CoreController` is implemented as a strict singleton to prevent multiple connections to the core process. In `lib/core/controller.dart`, the class defines a private static field `_instance` (lines 14-16) that holds the single instance of the controller.

The factory constructor `factory CoreController()` (lines 34-37) ensures that the first call creates the internal instance via `CoreController._internal()`, while all subsequent calls return the same object. For convenient global access throughout the codebase, FlClash exposes a top-level variable `coreController` (line 87) that holds this singleton instance.

## Platform-Specific Handler Selection

When the singleton is instantiated through `CoreController._internal` (lines 18-23), it performs platform detection to select the appropriate transport mechanism for Flutter-Go communication.

The implementation checks the platform and assigns the handler:

- **Android**: Uses `CoreLib` (an FFI wrapper) accessed via `coreLib!` (lines 19-21)
- **Desktop**: Uses `CoreService` (a socket/pipe client) accessed via `coreService!` (lines 22-23)

Both handlers implement the abstract mixin `CoreHandlerInterface` defined in `lib/core/interface.dart` (lines 77-84). This interface defines the complete set of RPC-style methods that the Go core exposes, including `init`, `shutdown`, `getProxies`, `changeProxy`, and runtime metrics methods.

## Unified API Surface for Core Operations

`CoreController` forwards every public method call to the selected handler's `_interface`, keeping the rest of the Flutter codebase agnostic of the underlying transport mechanism.

The controller exposes high-level methods that map directly to Go core functionality:

- **Initialization and shutdown**: `init()`, `shutdown()` → `_interface.init(...)`, `_interface.shutdown(...)`
- **Configuration validation**: `validateConfig()` → `_interface.validateConfig(...)`
- **Proxy management**: `getProxiesGroups()`, `changeProxy()` → `_interface.getProxies()`, `_interface.changeProxy(...)`
- **Runtime metrics**: `getTraffic()`, `getTotalTraffic()`, `getMemory()` → corresponding handler calls
- **Event-driven actions**: `startLog()`, `stopLog()`, `requestGc()`, `crash()` → handler methods

This architecture ensures that UI components and business logic never need to know whether they are communicating via FFI or sockets.

## Lifecycle Coordination and Connection State

Beyond method forwarding, the controller manages the core-process lifecycle and connection state. It exposes the underlying handler's `completer` through a getter `bool get isCompleted` (line 39), allowing other components to verify whether the core connection is fully established before issuing commands.

This is critical for startup sequences where the UI must wait for the Go core to initialize completely before requesting proxy lists or traffic statistics.

## Implementation Files and Architecture

The Flutter-Go communication architecture spans four key files in the `lib/core/` directory:

| File | Role |
|------|------|
| `lib/core/controller.dart` | Singleton that selects the correct handler and forwards all calls |
| `lib/core/interface.dart` | Defines `CoreHandlerInterface` with all RPC method signatures |
| `lib/core/lib.dart` | Android FFI implementation (`CoreLib`) |
| `lib/core/service.dart` | Desktop socket/pipe implementation (`CoreService`) |

## Practical Usage Examples

Below are common patterns for interacting with the Go core through the `CoreController` singleton:

```dart
// Initialize the core with the current configuration version
await coreController.init(appVersion);

// Validate a user-provided configuration file
final validationMessage = await coreController.validateConfig('/path/to/config.yaml');

// Switch the active proxy
await coreController.changeProxy(ChangeProxyParams(
  name: 'Proxy-Group-1',
  proxy: '🇺🇸 US-Node',
));

// Retrieve traffic statistics for UI display
final traffic = await coreController.getTraffic(onlyStatisticsProxy: true);

```

## Summary

- The `CoreController` singleton in FlClash (`lib/core/controller.dart`) is the exclusive bridge between Flutter and the Clash Meta Go core.
- It implements the singleton pattern using a private `_instance` field and factory constructor to ensure only one core connection exists.
- The controller automatically selects between `CoreLib` (FFI for Android) and `CoreService` (sockets for Desktop) during initialization.
- All RPC-style methods—initialization, proxy management, traffic metrics, and lifecycle control—are unified through the `CoreHandlerInterface` abstraction.
- The `isCompleted` getter exposes the connection state's completer, allowing components to synchronize with the core's readiness.

## Frequently Asked Questions

### What is the CoreController singleton in FlClash?

The `CoreController` is a singleton class in `lib/core/controller.dart` that serves as the single point of entry for all communication between FlClash's Flutter UI and the Clash Meta core written in Go. It ensures that only one connection to the core exists throughout the application lifecycle and provides a platform-agnostic API for calling Go functions from Dart.

### How does FlClash handle different platforms in CoreController?

During instantiation via `CoreController._internal` (lines 18-23), the controller checks the current platform and selects the appropriate handler implementation. For Android, it uses `CoreLib` (an FFI wrapper accessed via `coreLib!`), while desktop platforms use `CoreService` (a socket/pipe client accessed via `coreService!`). Both implement the same `CoreHandlerInterface`, ensuring consistent behavior across platforms.

### What core operations can be performed through CoreController?

The controller exposes the full range of Clash Meta core operations defined in `CoreHandlerInterface`, including `init` and `shutdown` for lifecycle management, `validateConfig` for configuration checking, `getProxies` and `changeProxy` for proxy management, and telemetry methods like `getTraffic`, `getTotalTraffic`, and `getMemory`. It also supports logging controls (`startLog`, `stopLog`), garbage collection requests (`requestGc`), and crash reporting (`crash`).

### When should I check the isCompleted property?

You should check `coreController.isCompleted` (which exposes the underlying handler's completer state at line 39) before issuing commands that require an active core connection, such as fetching proxy lists or traffic statistics. This boolean getter indicates whether the connection to the Go core is fully established, preventing errors from premature API calls during application startup.