flutter_singbox_vpn 1.1.1
flutter_singbox_vpn: ^1.1.1 copied to clipboard
A Flutter plugin for Sing-Box VPN by TecClub. Provides complete VPN functionality with traffic stats, logging, and per-app tunneling.
Flutter SingBox #
A Flutter plugin for integrating Sing-Box VPN functionality into your Flutter applications. This plugin provides a complete bridge to the native Sing-Box implementation on Android.
Features #
- ✅ VPN Connection Management - Start, stop, and monitor VPN connections
- ✅ Real-time Status Updates - Stream-based status monitoring
- ✅ Traffic Statistics - Upload/download speeds, data usage, connection counts
- ✅ Log Streaming - Real-time logs from sing-box core
- ✅ Per-App Tunneling - Include/exclude specific apps from VPN
- ✅ 16KB Page Size Support - Compatible with Android 15+ devices
- ✅ JSON Configuration - Full sing-box JSON configuration support
Platform Support #
| Platform | Support | Sing-Box Version |
|---|---|---|
| Android | ✅ | 1.12.12 |
| iOS | ❌ Not Supported | - |
| macOS | ❌ Not Supported | - |
| Windows | ❌ Not Supported | - |
| Linux | ❌ Not Supported | - |
Need iOS, macOS, or Windows support?
Contact us at tecclubx.com or email [email protected] for custom development.
Requirements #
- Flutter SDK:
>=3.3.0 - Dart SDK:
>=3.9.2 - Android:
minSdk 21,targetSdk 36
Installation #
Add the plugin to your pubspec.yaml:
dependencies:
flutter_singbox: ^1.1.0
Then run:
flutter pub get
Android Setup #
1. Update android/app/build.gradle.kts #
android {
compileSdk = 36
defaultConfig {
minSdk = 21
targetSdk = 36
}
packaging {
jniLibs {
useLegacyPackaging = true
}
}
}
2. Update android/app/src/main/AndroidManifest.xml #
Add the required permissions:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- Required Permissions -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application
android:label="Your App"
android:extractNativeLibs="true"
tools:targetApi="36">
<!-- Your activities here -->
</application>
</manifest>
3. Update android/settings.gradle.kts #
Add JitPack repository for sing-box library:
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}
Usage #
Initialize the Plugin #
import 'package:flutter_singbox/flutter_singbox.dart';
final flutterSingbox = FlutterSingbox();
Save Configuration #
Provide a valid sing-box JSON configuration:
String config = '''
{
"dns": {
"servers": [
{
"address": "tls://8.8.8.8",
"tag": "dns-remote"
}
]
},
"inbounds": [
{
"type": "tun",
"tag": "tun-in",
"address": ["172.19.0.1/30"],
"auto_route": true,
"strict_route": true
}
],
"outbounds": [
{
"type": "hysteria2",
"tag": "proxy",
"server": "your-server.com",
"server_port": 443,
"password": "your-password"
},
{
"type": "direct",
"tag": "direct"
}
]
}
''';
await flutterSingbox.saveConfig(config);
Start/Stop VPN #
// Start VPN connection
bool started = await flutterSingbox.startVPN();
// Stop VPN connection
bool stopped = await flutterSingbox.stopVPN();
// Get current status
String status = await flutterSingbox.getVPNStatus();
// Returns: "Stopped", "Starting", "Started", or "Stopping"
Listen to Status Changes #
flutterSingbox.onStatusChanged.listen((statusMap) {
String status = statusMap['status'];
int statusCode = statusMap['statusCode'];
print('VPN Status: $status (code: $statusCode)');
});
Status codes:
0- Stopped1- Starting2- Started3- Stopping
Monitor Traffic Statistics #
flutterSingbox.onTrafficUpdate.listen((stats) {
// Raw values (in bytes)
int uploadSpeed = stats['uplinkSpeed'];
int downloadSpeed = stats['downlinkSpeed'];
int uploadTotal = stats['uplinkTotal'];
int downloadTotal = stats['downlinkTotal'];
// Formatted strings
String uploadSpeedStr = stats['formattedUplinkSpeed']; // e.g., "1.24 KB/s"
String downloadSpeedStr = stats['formattedDownlinkSpeed']; // e.g., "5.67 MB/s"
String uploadTotalStr = stats['formattedUplinkTotal']; // e.g., "125.4 MB"
String downloadTotalStr = stats['formattedDownlinkTotal']; // e.g., "1.2 GB"
// Connection counts
int connectionsIn = stats['connectionsIn'];
int connectionsOut = stats['connectionsOut'];
});
Stream Logs #
flutterSingbox.onLogMessage.listen((logEvent) {
if (logEvent['type'] == 'log') {
String message = logEvent['message'];
print('Log: $message');
}
});
// Get buffered logs
List<String> logs = await flutterSingbox.getLogs();
// Clear log buffer
await flutterSingbox.clearLogs();
Per-App Tunneling #
// Set mode: "off", "include", or "exclude"
await flutterSingbox.setPerAppProxyMode(ProxyMode.EXCLUDE);
// Set app list (package names)
await flutterSingbox.setPerAppProxyList([
'com.whatsapp',
'com.instagram.android',
]);
// Get current settings
String mode = await flutterSingbox.getPerAppProxyMode();
List<String> apps = await flutterSingbox.getPerAppProxyList();
// Get installed apps for selection UI
List<Map<String, dynamic>> installedApps = await flutterSingbox.getInstalledApps();
for (var app in installedApps) {
print('${app['appName']} - ${app['packageName']}');
}
Complete Example #
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_singbox/flutter_singbox.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final FlutterSingbox _singbox = FlutterSingbox();
String _status = 'Stopped';
String _uploadSpeed = '0 B/s';
String _downloadSpeed = '0 B/s';
StreamSubscription? _statusSub;
StreamSubscription? _trafficSub;
@override
void initState() {
super.initState();
_initVPN();
}
void _initVPN() {
// Listen to status changes
_statusSub = _singbox.onStatusChanged.listen((status) {
setState(() => _status = status['status']);
});
// Listen to traffic updates
_trafficSub = _singbox.onTrafficUpdate.listen((stats) {
setState(() {
_uploadSpeed = stats['formattedUplinkSpeed'];
_downloadSpeed = stats['formattedDownlinkSpeed'];
});
});
}
Future<void> _toggleVPN() async {
if (_status == VPNStatus.STOPPED) {
await _singbox.saveConfig(yourConfigJson);
await _singbox.startVPN();
} else if (_status == VPNStatus.STARTED) {
await _singbox.stopVPN();
}
}
@override
void dispose() {
_statusSub?.cancel();
_trafficSub?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('SingBox VPN')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Status: $_status'),
SizedBox(height: 16),
Text('↑ $_uploadSpeed'),
Text('↓ $_downloadSpeed'),
SizedBox(height: 32),
ElevatedButton(
onPressed: _toggleVPN,
child: Text(_status == VPNStatus.STOPPED ? 'Connect' : 'Disconnect'),
),
],
),
),
),
);
}
}
API Reference #
FlutterSingbox Class #
| Method | Description | Returns |
|---|---|---|
saveConfig(String config) |
Save sing-box JSON configuration | Future<bool> |
getConfig() |
Get current configuration | Future<String> |
startVPN() |
Start VPN connection | Future<bool> |
stopVPN() |
Stop VPN connection | Future<bool> |
getVPNStatus() |
Get current VPN status | Future<String> |
setPerAppProxyMode(String mode) |
Set per-app tunneling mode | Future<bool> |
getPerAppProxyMode() |
Get per-app tunneling mode | Future<String> |
setPerAppProxyList(List<String>? apps) |
Set apps for per-app tunneling | Future<bool> |
getPerAppProxyList() |
Get per-app tunneling app list | Future<List<String>> |
getInstalledApps() |
Get list of installed apps | Future<List<Map>> |
getLogs() |
Get buffered log messages | Future<List<String>> |
clearLogs() |
Clear log buffer | Future<bool> |
Streams #
| Stream | Description | Event Type |
|---|---|---|
onStatusChanged |
VPN status updates | Map<String, dynamic> |
onTrafficUpdate |
Traffic statistics | Map<String, dynamic> |
onLogMessage |
Log messages from sing-box | Map<String, dynamic> |
Helper Classes #
class VPNStatus {
static const String STOPPED = "Stopped";
static const String STARTING = "Starting";
static const String STARTED = "Started";
static const String STOPPING = "Stopping";
}
class ProxyMode {
static const String OFF = "off";
static const String INCLUDE = "include";
static const String EXCLUDE = "exclude";
}
Troubleshooting #
VPN Permission Not Granted #
The plugin will automatically request VPN permission when startVPN() is called. Ensure your app handles the permission dialog properly.
No Internet After Connecting #
Check your sing-box configuration:
- Ensure DNS servers are properly configured
- Verify outbound proxy servers are reachable
- Check route rules are correct
Build Errors #
If you encounter build errors:
- Clean and rebuild:
cd android && ./gradlew clean && cd ..
flutter clean
flutter pub get
flutter build apk
- Ensure JitPack repository is added to
settings.gradle.kts
16KB Page Size (Android 15+) #
The plugin supports 16KB page size devices. Ensure your app's build.gradle.kts has:
android {
compileSdk = 36
packaging {
jniLibs {
useLegacyPackaging = true
}
}
}
Changelog #
1.1.1 #
- Fixed app launcher icon being overridden by plugin drawable
- Added VPN key icon for notification
- Fixed VPN status incorrectly showing "Started" when config has errors
- Improved error handling during VPN startup
1.1.0 #
- Added log streaming support
- Added 16KB page size support for Android 15+
- Improved VPN state management
- Fixed connection stability issues
- Updated to libbox 1.12.12
1.0.0 #
- Initial release
License #
This project is licensed under the MIT License - see the LICENSE file for details.
Credits #
Contact #
For support, custom development, or business inquiries:
- 🌐 Website: tecclubx.com
- 📧 Email: [email protected]
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.