vex_channel 1.0.0 copy "vex_channel: ^1.0.0" to clipboard
vex_channel: ^1.0.0 copied to clipboard

VexChannel — Zero-boilerplate platform channels for Flutter. Write pure Dart, access every native API on Android, iOS, macOS, Windows, Linux and Web without touching Kotlin/Swift/C++ once.

⚡ VexChannel #

Zero-boilerplate Flutter platform channels.
Write pure Dart. Access every native API on Android, iOS, macOS, Windows, Linux and Web — without writing a single line of Kotlin, Swift, or C++.


Why VexChannel? #

Traditional Flutter platform channels require you to:

  1. Write a MethodChannel in Dart
  2. Write matching native code in Kotlin/Java (Android)
  3. Write matching native code in Swift/ObjC (iOS)
  4. Repeat for every platform you target

VexChannel eliminates all of that.

You write Dart. VexChannel handles routing, type coercion, error mapping, retry logic, caching, timeouts, and streaming — for every platform simultaneously.


Installation #

dependencies:
  vex_channel: ^1.0.0

Quick Start #

Option A — Use a built-in module (zero setup) #

import 'package:vex_channel/vex_channel.dart';

// Battery
final battery = VexChannel.bridge(VexBattery.instance);
final level   = await battery.level;           // 87.5
final info    = await battery.info;            // VexBatteryInfo
battery.stream.listen((info) { ... });         // real-time stream

// Device Info
final device = VexChannel.bridge(VexDeviceInfoModule.instance);
final model  = await device.model;             // "Pixel 8 Pro"

// Haptics
final haptics = VexChannel.bridge(VexHaptics.instance);
await haptics.success;                         // 🎉 success haptic

// Clipboard
final cb = VexChannel.bridge(VexClipboard.instance);
await cb.setText('Hello VexChannel!');

// Connectivity
final conn = VexChannel.bridge(VexConnectivity.instance);
print(await conn.isWifi);                      // true
conn.onConnectivityChanged.listen((type) { ... });

// Sensors
final sensors = VexChannel.bridge(VexSensors.instance);
sensors.accelerometer.listen((data) {
  print('x=${data.x} y=${data.y} z=${data.z}');
});

// Storage (key-value + secure)
final store = VexChannel.bridge(VexStorage.instance);
await store.setString('token', 'abc123');
await store.setSecureString('api_key', 's3cr3t');
final token = await store.getString('token');

// File System
final fs   = VexChannel.bridge(VexFileSystem.instance);
final docs = await fs.documentsDirectory;
await fs.writeAsString('$docs/note.txt', 'Hello!');
final text = await fs.readAsString('$docs/note.txt');

// Camera
final cam   = VexChannel.bridge(VexCameraModule.instance);
final photo = await cam.capturePhoto(quality: 90);   // returns file path

// Permissions
final perms = VexChannel.bridge(VexPermissions.instance);
final status = await perms.request(VexPermission.camera);
if (status == VexPermissionStatus.granted) { ... }

// Location
final loc = VexChannel.bridge(VexLocationModule.instance);
final pos = await loc.getCurrentLocation();
print('${pos.latitude}, ${pos.longitude}');

// Biometrics
final bio = VexChannel.bridge(VexBiometrics.instance);
final ok  = await bio.authenticate(reason: 'Confirm your identity');

// Notifications
final notif = VexChannel.bridge(VexNotifications.instance);
await notif.show(VexNotification(id: '1', title: 'Hello', body: 'From VexChannel'));

// Audio
final audio    = VexChannel.bridge(VexAudio.instance);
final playerId = await audio.loadUrl('https://example.com/song.mp3');
await audio.play(playerId);
audio.positionStream(playerId).listen((pos) => print(pos));

Option B — Create your OWN bridge (Dart only, no native changes) #

// 1. Extend VexBridgeBase — that's all the Dart side needs
class CameraBridge extends VexBridgeBase {
  @override
  String get channelName => 'com.myapp/camera';

  // 2. Call invokeMethod for one-shot calls
  Future<String?> capturePhoto({int quality = 90}) async {
    final res = await invokeMethod<String>(
      'capturePhoto',
      args: {'quality': quality},
    );
    return res.unwrapOr(null);
  }

  // 3. Call openStream for real-time events
  Stream<Map<String, dynamic>> get previewFrames =>
      openStream<Map<String, dynamic>>(eventName: 'preview');
}

// 4. Register and use
final camera = VexChannel.bridge(CameraBridge());
final path   = await camera.capturePhoto(quality: 95);
camera.previewFrames.listen((frame) { ... });

Option C — Low-level one-shot (no bridge class at all) #

// Invoke any native method on any channel in one line
final res = await VexChannel.invoke<double>(
  channel: 'com.myapp/battery',
  method:  'getLevel',
);
print(res.unwrap());   // 87.5

// Or with the shorthand (throws on error)
final level = await VexChannel.call<double>(
  channel: 'vex_channel/battery',
  method:  'getBatteryLevel',
);

Option D — Native → Dart callbacks #

// Register a handler that native can call at any time
VexChannel.registerHandler(
  channel: 'com.myapp/push',
  method:  'onMessage',
  handler: (args) async {
    final title = args?['title'];
    print('Push: $title');
    return {'handled': true};
  },
);

Built-in Modules #

Module Class Channel
Battery VexBattery vex_channel/battery
Device Info VexDeviceInfoModule vex_channel/device_info
Connectivity VexConnectivity vex_channel/connectivity
Sensors VexSensors vex_channel/sensors
Permissions VexPermissions vex_channel/permissions
Haptics VexHaptics vex_channel/haptics
Clipboard VexClipboard vex_channel/clipboard
Location VexLocationModule vex_channel/location
File System VexFileSystem vex_channel/file_system
Storage VexStorage vex_channel/storage
Notifications VexNotifications vex_channel/notifications
Biometrics VexBiometrics vex_channel/biometrics
Camera VexCameraModule vex_channel/camera
Audio VexAudio vex_channel/audio
Network (HTTP) VexNetworkModule vex_channel/network

VexResponse — never throw, always return #

Every invokeMethod returns a VexResponse<T>:

final res = await invokeMethod<double>('getLevel');

// Pattern match
res.when(
  success: (level) => print('Level: $level'),
  failure: (error)  => print('Error: ${error.message}'),
);

// Or unwrap (throws VexException on failure)
final level = res.unwrap();

// Or provide a fallback
final level = res.unwrapOr(0.0);

// Map the value
final percent = res.map((v) => '${v.toStringAsFixed(1)}%');

Error Types #

Exception Cause
VexException Generic native error
VexTimeoutException Call exceeded timeout
VexUnsupportedPlatformException Feature not available on current OS
VexPermissionDeniedException Native permission denied

Advanced Features #

Retry on transient failure #

final res = await invokeMethod<String>(
  'getToken',
  retryCount: 3,   // retry up to 3 times with exponential back-off
);

Per-call caching #

final res = await invokeMethod<Map<String, dynamic>>(
  'getDeviceInfo',
  cacheable: true,
  cacheTtl: Duration(hours: 24),  // cached for 24 hours
);

Custom timeout per call #

final res = await invokeMethod<String>(
  'authenticate',
  timeout: Duration(minutes: 2),
);

Multi-codec support #

@VexBridge('com.myapp/data', codec: VexCodecType.json)
class DataBridge extends VexBridgeBase { ... }

Platform detection #

if (VexChannel.isRunningOn(VexOS.android)) {
  // Android-specific path
}

switch (VexChannel.currentPlatform) {
  case VexOS.ios:     ...
  case VexOS.android: ...
  case VexOS.web:     ...
  default:            ...
}

Logging & debugging #

VexChannel.enableLogging(verbose: true);
// Prints every channel call with timing:
// [VexChannel] → vex_channel/battery#getBatteryLevel
// [VexChannel] ✓ vex_channel/battery#getBatteryLevel → 3ms

VexChannel.registry.debugDump();
// ╔══════════════════════════════════════
// ║ VexChannel Registry — 3 bridge(s)
// ║  • VexBattery → VexBattery
// ║  • VexConnectivity → VexConnectivity
// ╚══════════════════════════════════════

Writing Native Handlers (one-time setup) #

VexChannel ships with complete Android (Kotlin) and iOS (Swift) handler implementations for all built-in modules. You only need to write native code when adding your own custom channel.

Android (Kotlin) #

// Register in VexChannelPlugin.kt — one line
MethodChannel(messenger, "com.myapp/camera").setMethodCallHandler { call, result ->
    when (call.method) {
        "capturePhoto" -> {
            val quality = call.argument<Int>("quality") ?: 90
            // ... actual native camera code ...
            result.success("/path/to/photo.jpg")
        }
        else -> result.notImplemented()
    }
}

iOS (Swift) #

FlutterMethodChannel(name: "com.myapp/camera", binaryMessenger: messenger)
    .setMethodCallHandler { call, result in
        if call.method == "capturePhoto" {
            // ... actual native camera code ...
            result("/path/to/photo.jpg")
        } else {
            result(FlutterMethodNotImplemented)
        }
    }

That's literally all the native code you ever need — one handler per channel. VexChannel takes care of everything else.


License #

MIT © Mysterious Coder

2
likes
130
points
0
downloads

Documentation

Documentation
API reference

Publisher

verified publishermysteriouscoder.com

Weekly Downloads

VexChannel — Zero-boilerplate platform channels for Flutter. Write pure Dart, access every native API on Android, iOS, macOS, Windows, Linux and Web without touching Kotlin/Swift/C++ once.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

collection, crypto, flutter, flutter_web_plugins, json_annotation, path, plugin_platform_interface

More

Packages that depend on vex_channel

Packages that implement vex_channel