vex_channel 1.0.0
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.
// example/lib/main.dart
// VexChannel Example — demonstrates every major feature
import 'package:flutter/material.dart';
import 'package:vex_channel/vex_channel.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
VexChannel.enableLogging(verbose: true);
runApp(const VexExampleApp());
}
class VexExampleApp extends StatelessWidget {
const VexExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'VexChannel Demo',
theme: ThemeData(
colorSchemeSeed: Colors.deepPurple,
useMaterial3: true,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String _output = 'Tap a button to test VexChannel';
void _show(String msg) => setState(() => _output = msg);
// ── Battery ────────────────────────────────────────────────────────────────
Future<void> _getBattery() async {
final battery = VexChannel.bridge(VexBattery.instance);
final info = await battery.info;
_show('🔋 Battery: ${info.level.toStringAsFixed(1)}%\n'
'State: ${info.state.name}\n'
'Low power: ${info.isLowPowerMode}');
}
// ── Device Info ────────────────────────────────────────────────────────────
Future<void> _getDeviceInfo() async {
final d = VexChannel.bridge(VexDeviceInfoModule.instance);
final info = await d.get();
_show('📱 Device: ${info.brand} ${info.model}\n'
'OS: ${info.systemName} ${info.systemVersion}\n'
'Physical: ${info.isPhysicalDevice}\n'
'SDK: ${info.sdkVersion}');
}
// ── Connectivity ───────────────────────────────────────────────────────────
Future<void> _getConnectivity() async {
final conn = VexChannel.bridge(VexConnectivity.instance);
final type = await conn.currentType;
final ssid = await conn.wifiSSID;
_show('📡 Network: ${type.name}\nSSID: ${ssid ?? "N/A"}');
}
// ── Haptics ────────────────────────────────────────────────────────────────
Future<void> _haptics() async {
final h = VexChannel.bridge(VexHaptics.instance);
await h.success;
_show('📳 Success haptic fired!');
}
// ── Clipboard ──────────────────────────────────────────────────────────────
Future<void> _clipboard() async {
final cb = VexChannel.bridge(VexClipboard.instance);
await cb.setText('Hello from VexChannel! 🚀');
final text = await cb.getText();
_show('📋 Clipboard: $text');
}
// ── Storage ────────────────────────────────────────────────────────────────
Future<void> _storage() async {
final store = VexChannel.bridge(VexStorage.instance);
await store.setString('vex_demo_key', 'VexChannel rocks!');
final val = await store.getString('vex_demo_key');
await store.setInt('launch_count', 42);
final count = await store.getInt('launch_count');
_show('💾 Storage:\nKey: $val\nLaunch count: $count');
}
// ── Sensors stream ─────────────────────────────────────────────────────────
Future<void> _sensors() async {
final sensors = VexChannel.bridge(VexSensors.instance);
_show('🌀 Listening to accelerometer...');
sensors.accelerometer.take(5).listen(
(data) => _show('🌀 Accel: x=${data.x.toStringAsFixed(2)} '
'y=${data.y.toStringAsFixed(2)} z=${data.z.toStringAsFixed(2)}'),
);
}
// ── File System ────────────────────────────────────────────────────────────
Future<void> _fileSystem() async {
final fs = VexChannel.bridge(VexFileSystem.instance);
final docs = await fs.documentsDirectory;
final path = '$docs/vex_test.txt';
await fs.writeAsString(path, 'VexChannel file I/O test ✅');
final content = await fs.readAsString(path);
final size = await fs.fileSize(path);
_show('📁 Docs: $docs\nWritten & read: $content\nSize: $size bytes');
}
// ── Permissions ────────────────────────────────────────────────────────────
Future<void> _permissions() async {
final perms = VexChannel.bridge(VexPermissions.instance);
final camera = await perms.check(VexPermission.camera);
final location = await perms.check(VexPermission.location);
_show('🔐 Camera: ${camera.name}\nLocation: ${location.name}');
}
// ── Low-level raw invoke (no bridge) ──────────────────────────────────────
Future<void> _rawInvoke() async {
final res = await VexChannel.invoke<double>(
channel: 'vex_channel/battery',
method: 'getBatteryLevel',
);
res.when(
success: (v) => _show('⚡ Raw invoke: battery=$v%'),
failure: (e) => _show('✗ Error: $e'),
);
}
// ── Custom bridge (zero-boilerplate pattern demo) ─────────────────────────
Future<void> _customBridge() async {
final bridge = VexChannel.bridge(MyCustomBridge());
final result = await bridge.getAppVersion();
_show('🎯 Custom Bridge: version=$result\n'
'Platform: ${VexChannel.currentPlatform.name}');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('⚡ VexChannel'),
backgroundColor: Colors.deepPurple,
foregroundColor: Colors.white,
),
body: Column(
children: [
Expanded(
child: Container(
width: double.infinity,
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.deepPurple.withValues(alpha: 0.3)),
),
child: SingleChildScrollView(
child: Text(_output, style: const TextStyle(fontFamily: 'monospace')),
),
),
),
Expanded(
child: GridView.count(
crossAxisCount: 3,
padding: const EdgeInsets.all(12),
mainAxisSpacing: 8,
crossAxisSpacing: 8,
children: [
_Btn('🔋 Battery', _getBattery, Colors.orange),
_Btn('📱 Device', _getDeviceInfo, Colors.blue),
_Btn('📡 Network', _getConnectivity, Colors.green),
_Btn('📳 Haptics', _haptics, Colors.purple),
_Btn('📋 Clipboard', _clipboard, Colors.teal),
_Btn('💾 Storage', _storage, Colors.red),
_Btn('🌀 Sensors', _sensors, Colors.indigo),
_Btn('📁 Files', _fileSystem, Colors.brown),
_Btn('🔐 Perms', _permissions, Colors.pink),
_Btn('⚡ Raw', _rawInvoke, Colors.amber),
_Btn('🎯 Custom', _customBridge, Colors.cyan),
],
),
),
],
),
);
}
}
class _Btn extends StatelessWidget {
final String label;
final VoidCallback onTap;
final Color color;
const _Btn(this.label, this.onTap, this.color);
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onTap,
style: ElevatedButton.styleFrom(
backgroundColor: color,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
),
child: Text(label, textAlign: TextAlign.center, style: const TextStyle(fontSize: 12)),
);
}
}
// ─── Custom bridge example ─────────────────────────────────────────────────
/// Shows how a developer creates their OWN bridge with VexChannel:
/// 1. Extend VexBridgeBase
/// 2. Set channelName
/// 3. Call invokeMethod / openStream
/// No native code changes needed for the Dart side!
class MyCustomBridge extends VexBridgeBase {
@override
String get channelName => 'com.myapp/custom';
Future<String> getAppVersion() async {
// This calls native — the developer only writes Dart
final res = await invokeMethod<String>('getAppVersion');
return res.unwrapOr('1.0.0');
}
Stream<Map<String, dynamic>> get liveEvents =>
openStream<Map<String, dynamic>>(eventName: 'liveEvents');
}