9.9 KiB
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 PlatformInterfacewith 33 method signatures - Depends on:
plugin_platform_interfacepackage,ch34_types.dart - Used by:
MethodChannelCh34for 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 Ch34Platformwith actual channel communication - Channels:
MethodChannel('ch34')- synchronous method callsEventChannel('ch34/data')- serial data streamEventChannel('ch34/modem')- modem status streamEventChannel('ch34/usb_state')- USB hotplug events
- Depends on:
dart:async,flutter/services.dart - Used by:
Ch34Manager(viaCh34Platform.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 inch34_method_channel.dart)
- Enums:
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, MethodCallHandlerCh34DataStreamHandler.java- Data EventChannel stream handler with WCH callback bridgingCh34ModemStreamHandler.java- Modem status EventChannel stream handlerCh34UsbStateStreamHandler.java- USB hotplug EventChannel with BroadcastReceiverCh34TypeConverter.java- Type conversion between Dart and WCH types
- Depends on:
CH34XUARTDriver.jar(bundled inandroid/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
- Consumer calls
Ch34Manager.enumDevice() - Delegates to
Ch34Platform.instance.enumDevice() MethodChannelCh34.enumDevice()invokesmethodChannel.invokeMethod('enumDevice')Ch34Plugin.enumDevice()callsWCHUARTManager.enumDevice()on native side- Native iterates devices, extracts VID/PID/serialCount/chipType
- Returns
List<Map>through MethodChannel - Dart side deserializes into
List<UsbDeviceInfo>
Data Read Flow (Callback Mode)
- Consumer calls
Ch34Manager.registerDataCallback(deviceName, serialNumber, onData) MethodChannelCh34subscribes toEventChannel('ch34/data')- Invokes
methodChannel.invokeMethod('registerDataCallback') Ch34PluginregistersCh34DataStreamHandler.getWchCallback()withWCHUARTManager.registerDataCallback()- When data arrives, WCH triggers
IDataCallback.onData() Ch34DataStreamHandlerbridges data to EventChannel- Dart receives via
StreamSubscription, invokes user callback
Data Write Flow
- Consumer calls
Ch34Manager.writeData(deviceName, serialNumber, data) - Delegates through platform interface to
MethodChannelCh34 methodChannel.invokeMethod('writeData', {data: data, ...})Ch34Plugin.writeData()callsWCHUARTManager.writeData(device, serial, data, length, timeout)- Returns bytes written count
USB Hotplug Event Flow
Ch34UsbStateStreamHandlerregistersBroadcastReceiverforACTION_USB_DEVICE_ATTACHED/DETACHED- Android system sends broadcast on USB events
- BroadcastReceiver extracts
UsbDeviceand callsnotifyStateChanged() - EventChannel sends
{deviceName, connected}to Dart MethodChannelCh34invokes_usbStateCallback
Device Open Flow
- Consumer calls
Ch34Manager.openDevice(deviceName) Ch34Plugin.openDevice()attemptsWCHUARTManager.openDevice(device)- If
NoPermissionException, requests permission then retries after 2s delay - On success, adds to
openedDevicesmap and notifiesusbStateStreamHandler
State Management
Native State:
WCHUARTManager- Singleton instance, lazy-initialized inensureManagerInitialized()openedDevices: Map<String, UsbDevice>- tracks currently opened devices- Three
EventChannel.StreamHandlerinstances - each manages its ownEventSink
Dart State:
Ch34Platform._instance- Singleton platform instanceMethodChannelCh34maintains_dataSubscription,_modemSubscription,_usbStateSubscription- StreamSubscriptions for event channels- Callback references:
_dataCallback,_modemCallback,_usbStateCallback
State Cleanup:
Ch34Plugin.close()callscloseAllDevices()to disconnect all and clear mapCh34UsbStateStreamHandler.onCancel()unregisters BroadcastReceiverCh34Manager.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_interfacepackage - Pattern:
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:
static Future<List<UsbDeviceInfo>> 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, andCh34Exception
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.yamldeclarespackage: com.example.ch34,pluginClass: Ch34Plugin
MethodChannel Dispatcher
- Location:
D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.javaline 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.
- Error codes:
- Dart: Default fallbacks on null results (e.g.,
result ?? false,result ?? Uint8List(0)) - Exception class:
Ch34ExceptioninD:\code\new_git_code\flutter\ch34\lib\src\ch34_method_channel.dartfor 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