# Architecture **Analysis Date:** 2026-04-16 ## Pattern Overview **Overall:** Flutter Platform Plugin (Federated Plugin Pattern) This is an Android-only Flutter plugin that wraps the WCH CH34X UART library (`CH34XUARTDriver.jar`). It bridges Flutter/Dart code to native Android USB serial communication via `MethodChannel` and `EventChannel`. **Key Characteristics:** - Static facade pattern for public API (`Ch34Manager`) - Platform interface pattern with token-based verification (`Ch34Platform`) - MethodChannel for command invocation, EventChannel for streaming callbacks - Single platform implementation (Android only, Kotlin/Java) - Singleton WCH UART manager on the native side ## Layers ### Dart Public API Layer - **Purpose:** Static facade providing the consumer-facing API - **Location:** `D:\code\new_git_code\flutter\ch34\lib\src\ch34_manager.dart` - **Contains:** Static methods delegating to `Ch34Platform.instance` - **Depends on:** `Ch34Platform` - **Used by:** Flutter app consuming the plugin ### Dart Platform Interface Layer - **Purpose:** Abstract interface defining all plugin methods with token verification - **Location:** `D:\code\new_git_code\flutter\ch34\lib\src\ch34_platform_interface.dart` - **Contains:** `abstract class Ch34Platform extends PlatformInterface` with 33 method signatures - **Depends on:** `plugin_platform_interface` package, `ch34_types.dart` - **Used by:** `MethodChannelCh34` for concrete implementation ### Dart MethodChannel Implementation Layer - **Purpose:** Bridges abstract platform interface to native Android via Flutter's platform channels - **Location:** `D:\code\new_git_code\flutter\ch34\lib\src\ch34_method_channel.dart` - **Contains:** `class MethodChannelCh34 extends Ch34Platform` with actual channel communication - **Channels:** - `MethodChannel('ch34')` - synchronous method calls - `EventChannel('ch34/data')` - serial data stream - `EventChannel('ch34/modem')` - modem status stream - `EventChannel('ch34/usb_state')` - USB hotplug events - **Depends on:** `dart:async`, `flutter/services.dart` - **Used by:** `Ch34Manager` (via `Ch34Platform.instance`) ### Dart Types Layer - **Purpose:** Shared data types and enums for the plugin - **Location:** `D:\code\new_git_code\flutter\ch34\lib\src\types\ch34_types.dart` - **Contains:** - Enums: `DataBits`, `StopBits`, `Parity`, `GpioDirection`, `GpioValue`, `SerialErrorType` - Data classes: `GpioStatus`, `SerialParameter`, `ModemStatus`, `UsbDeviceInfo` - Exception: `Ch34Exception` (defined in `ch34_method_channel.dart`) ### Android Native Layer - **Purpose:** Actual WCH UART library interaction, USB device management - **Location:** `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\` - **Contains:** - `Ch34Plugin.java` - Main plugin entry point, MethodCallHandler - `Ch34DataStreamHandler.java` - Data EventChannel stream handler with WCH callback bridging - `Ch34ModemStreamHandler.java` - Modem status EventChannel stream handler - `Ch34UsbStateStreamHandler.java` - USB hotplug EventChannel with BroadcastReceiver - `Ch34TypeConverter.java` - Type conversion between Dart and WCH types - **Depends on:** `CH34XUARTDriver.jar` (bundled in `android/libs/`) - **Used by:** Dart MethodChannel layer ### Backward Compatibility Layer - **Location:** `D:\code\new_git_code\flutter\ch34\lib\ch34_platform_interface.dart`, `D:\code\new_git_code\flutter\ch34\lib\ch34_method_channel.dart` - **Purpose:** Re-exports from `src/` for consumers using old import paths ## Data Flow ### Device Discovery Flow 1. Consumer calls `Ch34Manager.enumDevice()` 2. Delegates to `Ch34Platform.instance.enumDevice()` 3. `MethodChannelCh34.enumDevice()` invokes `methodChannel.invokeMethod('enumDevice')` 4. `Ch34Plugin.enumDevice()` calls `WCHUARTManager.enumDevice()` on native side 5. Native iterates devices, extracts VID/PID/serialCount/chipType 6. Returns `List` through MethodChannel 7. Dart side deserializes into `List` ### Data Read Flow (Callback Mode) 1. Consumer calls `Ch34Manager.registerDataCallback(deviceName, serialNumber, onData)` 2. `MethodChannelCh34` subscribes to `EventChannel('ch34/data')` 3. Invokes `methodChannel.invokeMethod('registerDataCallback')` 4. `Ch34Plugin` registers `Ch34DataStreamHandler.getWchCallback()` with `WCHUARTManager.registerDataCallback()` 5. When data arrives, WCH triggers `IDataCallback.onData()` 6. `Ch34DataStreamHandler` bridges data to EventChannel 7. Dart receives via `StreamSubscription`, invokes user callback ### Data Write Flow 1. Consumer calls `Ch34Manager.writeData(deviceName, serialNumber, data)` 2. Delegates through platform interface to `MethodChannelCh34` 3. `methodChannel.invokeMethod('writeData', {data: data, ...})` 4. `Ch34Plugin.writeData()` calls `WCHUARTManager.writeData(device, serial, data, length, timeout)` 5. Returns bytes written count ### USB Hotplug Event Flow 1. `Ch34UsbStateStreamHandler` registers `BroadcastReceiver` for `ACTION_USB_DEVICE_ATTACHED/DETACHED` 2. Android system sends broadcast on USB events 3. BroadcastReceiver extracts `UsbDevice` and calls `notifyStateChanged()` 4. EventChannel sends `{deviceName, connected}` to Dart 5. `MethodChannelCh34` invokes `_usbStateCallback` ### Device Open Flow 1. Consumer calls `Ch34Manager.openDevice(deviceName)` 2. `Ch34Plugin.openDevice()` attempts `WCHUARTManager.openDevice(device)` 3. If `NoPermissionException`, requests permission then retries after 2s delay 4. On success, adds to `openedDevices` map and notifies `usbStateStreamHandler` ## State Management **Native State:** - `WCHUARTManager` - Singleton instance, lazy-initialized in `ensureManagerInitialized()` - `openedDevices: Map` - tracks currently opened devices - Three `EventChannel.StreamHandler` instances - each manages its own `EventSink` **Dart State:** - `Ch34Platform._instance` - Singleton platform instance - `MethodChannelCh34` maintains `_dataSubscription`, `_modemSubscription`, `_usbStateSubscription` - StreamSubscriptions for event channels - Callback references: `_dataCallback`, `_modemCallback`, `_usbStateCallback` **State Cleanup:** - `Ch34Plugin.close()` calls `closeAllDevices()` to disconnect all and clear map - `Ch34UsbStateStreamHandler.onCancel()` unregisters BroadcastReceiver - `Ch34Manager.close()` triggers platform cleanup and cancels all subscriptions ## Key Abstractions ### Platform Interface (`Ch34Platform`) - **Purpose:** Abstract contract that decouples Flutter code from native implementation - **Example:** `D:\code\new_git_code\flutter\ch34\lib\src\ch34_platform_interface.dart` - **Pattern:** Token-based platform interface verification from `plugin_platform_interface` package - **Pattern:** ```dart static final Object _token = Object(); static Ch34Platform get instance { ... } static void registerDefaultInstance(Ch34Platform instance) { PlatformInterface.verifyToken(instance, _token); _instance = instance; } ``` ### Manager Facade (`Ch34Manager`) - **Purpose:** Static facade simplifying API usage for consumers - **Example:** `D:\code\new_git_code\flutter\ch34\lib\src\ch34_manager.dart` - **Pattern:** All static methods, all delegate to `Ch34Platform.instance` - **Pattern:** ```dart static Future> enumDevice() { return Ch34Platform.instance.enumDevice(); } ``` ### Type Converter (`Ch34TypeConverter`) - **Purpose:** Bridge between Dart enum indices and WCH native enum values - **Example:** `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34TypeConverter.java` - **Pattern:** Static utility class with conversion methods for GPIO direction/value, serial error types ## Entry Points ### Dart Library Entry - **Location:** `D:\code\new_git_code\flutter\ch34\lib\ch34.dart` - **Triggers:** Consumer imports `package:ch34/ch34.dart` - **Responsibilities:** Exports `Ch34Manager`, `Ch34Platform`, all types, and `Ch34Exception` ### Plugin Initialization (Android) - **Location:** `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` - **Trigger:** Flutter engine attaches plugin via `onAttachedToEngine()` - **Responsibilities:** Creates MethodChannel, 3 EventChannels, sets MethodCallHandler - **Declaration:** `D:\code\new_git_code\flutter\ch34\android\build.gradle` + `pubspec.yaml` declares `package: com.example.ch34`, `pluginClass: Ch34Plugin` ### MethodChannel Dispatcher - **Location:** `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` line 80-187 - **Trigger:** Dart invokes `methodChannel.invokeMethod()` - **Responsibilities:** Switch dispatch to 33 method handlers, error handling wrapper ## Error Handling **Strategy:** Native exceptions caught and converted to MethodChannel error responses with error codes; Dart side uses null-coalescing defaults and `Ch34Exception` for explicit failures. **Patterns:** - **Native:** Each method handler has try-catch, returns `result.error(ERROR_CODE, message, null)` - Error codes: `ENUM_DEVICE_FAILED`, `OPEN_DEVICE_FAILED`, `PERMISSION_DENIED`, `WRITE_DATA_FAILED`, etc. - **Dart:** Default fallbacks on null results (e.g., `result ?? false`, `result ?? Uint8List(0)`) - **Exception class:** `Ch34Exception` in `D:\code\new_git_code\flutter\ch34\lib\src\ch34_method_channel.dart` for throwing on Dart side - **Logging:** All errors logged via `Log.e(TAG, "...")` on Android native side ## Cross-Cutting Concerns **Logging:** Android `android.util.Log` with per-class TAGs (`Ch34Plugin`, `Ch34DataStream`, `Ch34ModemStream`, `Ch34UsbStateStream`). Dart side uses `flutter/foundation.dart` `debugPrint()` for callback errors. **Validation:** Device name resolution via `getDeviceOrThrow()` which searches `openedDevices` map first, then falls back to `manager.enumDevice()` lookup. **Authentication/Permission:** USB permission flow handled in `openDevice()` - attempts open, if `NoPermissionException`, calls `manager.requestPermission()` then retries after 2-second delay. --- *Architecture analysis: 2026-04-16*