sing_box 0.0.1
sing_box: ^0.0.1 copied to clipboard
A Flutter plugin for sing-box, providing platform-specific implementations for Android and iOS. Supports VPN connection management, statistics monitoring, bypass rules, DNS configuration, and more.
sing_box #
A Flutter plugin for sing-box, providing platform-specific implementations for Android and iOS.
⚠️ Warning: This is an unstable project currently under testing. The API may change, and there may be bugs. Any help, feedback, and contributions are welcome!
Features #
- ✅ Platform-specific implementations for Android and iOS
- ✅ VPN connection management (connect/disconnect)
- ✅ Connection status monitoring (real-time Stream)
- ✅ Connection statistics (speed, bytes sent/received, ping, connection duration)
- ✅ Speed testing
- ✅ Ping measurement (current server)
- ✅ App and domain bypass management
- ✅ Subnet bypass management (CIDR notation)
- ✅ DNS servers management
- ✅ Server switching
- ✅ Settings persistence (auto-connect, auto-reconnect, kill switch)
- ✅ Multiple server configurations with different protocols
- ✅ Blocked apps and domains management
- ✅ Observer pattern for logging (with Talker integration support)
- ✅ Notifications from sing-box
Requirements #
Minimum Versions #
- Android: API 23+ (Android 6.0+)
- iOS: iOS 15.0+
- Flutter: 3.3.0 or higher
Development Tools #
- Flutter SDK: Install Flutter SDK (version 3.3.0 or higher)
- Android Studio / Xcode: For Android/iOS development
- Physical Device or Emulator: VPN functionality requires a real device or emulator
Installation #
Add Dependency #
Add this to your package's pubspec.yaml file:
dependencies:
sing_box: ^0.0.1
Then run:
flutter pub get
Platform Setup #
Android
-
Minimum SDK: Android API 23 (Android 6.0) or higher
-
Permissions: The plugin automatically declares required permissions in
AndroidManifest.xml:INTERNETBIND_VPN_SERVICEFOREGROUND_SERVICEPOST_NOTIFICATIONSACCESS_NETWORK_STATEACCESS_WIFI_STATE
-
VPN Permission: The plugin will request VPN permission when you call
connect(). The user must grant this permission for VPN to work.
iOS
-
Minimum iOS Version: iOS 15.0 or higher
-
Download Libbox.xcframework:
The iOS framework (
Libbox.xcframework) is distributed via GitHub Releases due to size limitations. You need to download it manually:Option 1: Using the installation script (recommended)
# In your Flutter project root # Note: Requires git-lfs (install with: brew install git-lfs) curl -L https://raw.githubusercontent.com/qusadprod/sing_box/main/install_ios_framework.sh | bashOption 2: Manual installation
# Clone repository (shallow clone to save time) git clone --depth=1 https://github.com/qusadprod/sing_box.git temp_repo # Copy framework mkdir -p ios/Frameworks cp -R temp_repo/ios/Frameworks/Libbox.xcframework ios/Frameworks/ # Cleanup rm -rf temp_repo -
Network Extension: The plugin uses Network Extension for VPN functionality. You need to:
- Enable Network Extension capability in Xcode
- Configure App Groups for communication between main app and extension
- The extension is automatically configured during build
-
Permissions: The plugin automatically handles required permissions.
Quick Start #
1. Initialize Plugin #
The plugin uses Singleton pattern and must be initialized before using other methods, preferably before application start:
import 'package:flutter/widgets.dart';
import 'package:sing_box/sing_box.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize plugin
await SingBox.instance.initialize();
runApp(MyApp());
}
2. Basic Connection #
import 'package:sing_box/sing_box.dart';
final singBox = SingBox.instance;
// Connect to VPN with JSON configuration
final config = '''
{
"outbounds": [
{
"type": "vmess",
"tag": "proxy",
"server": "example.com",
"server_port": 443,
"uuid": "your-uuid-here",
"security": "auto"
}
]
}
''';
final connected = await singBox.connect(config);
if (connected) {
print('Connected successfully!');
}
// Disconnect
await singBox.disconnect();
3. Monitor Connection Status #
// Listen to connection status changes
singBox.watchSingBoxConnectionStatus().listen((status) {
print('Status: ${status.name}');
// Status values: disconnected, connecting, connected, disconnecting,
// disconnectedByUser, connectionLost, error
});
4. Monitor Statistics #
// Listen to connection statistics in real-time
singBox.watchSingBoxConnectionStats().listen((stats) {
print('Download: ${stats.downloadSpeed} B/s');
print('Upload: ${stats.uploadSpeed} B/s');
print('Ping: ${stats.ping} ms');
print('Duration: ${stats.connectionDuration} ms');
});
API Documentation #
Initialization #
initialize()
Initialize the plugin. Must be called before using other methods.
Future<bool> initialize()
Returns: true if initialization successful, false otherwise.
Example:
final success = await SingBox.instance.initialize();
Platform Information #
getPlatformVersion()
Get the platform version string.
Future<String?> getPlatformVersion()
Returns: Platform version string (e.g., "Android 12" or "iOS 15.0").
Example:
final version = await singBox.getPlatformVersion();
print('Platform: $version');
VPN Connection #
connect(String config)
Connect to VPN with specified configuration.
Future<bool> connect(String config)
Parameters:
config(String): JSON string with server configuration (sing-box format)
Returns: true if connection started successfully, false otherwise.
Example:
final config = '{"outbounds": [{"type": "vmess", ...}]}';
final connected = await singBox.connect(config);
Note: On Android, this will request VPN permission if not already granted.
disconnect()
Disconnect from VPN.
Future<bool> disconnect()
Returns: true if disconnection started successfully, false otherwise.
Example:
await singBox.disconnect();
getConnectionStatus()
Get current connection status.
Future<SingBoxConnectionStatus> getConnectionStatus()
Returns: Current connection status enum.
Status Values:
disconnected- Not connectedconnecting- Connecting in progressconnected- Connecteddisconnecting- Disconnecting in progressdisconnectedByUser- Disconnected by userconnectionLost- Connection lost with servererror- Connection error
Example:
final status = await singBox.getConnectionStatus();
if (status == SingBoxConnectionStatus.connected) {
print('VPN is connected');
}
watchSingBoxConnectionStatus()
Subscribe to connection status changes. Returns a Stream that emits status updates.
Stream<SingBoxConnectionStatus> watchSingBoxConnectionStatus()
Returns: Stream of connection status updates.
Example:
singBox.watchSingBoxConnectionStatus().listen((status) {
switch (status) {
case SingBoxConnectionStatus.connected:
print('Connected!');
break;
case SingBoxConnectionStatus.disconnected:
print('Disconnected');
break;
// ... other cases
}
});
Connection Statistics #
getConnectionStats()
Get current connection statistics.
Future<SingBoxConnectionStats> getConnectionStats()
Returns: SingBoxConnectionStats object with:
downloadSpeed(int): Current download speed in bytes per seconduploadSpeed(int): Current upload speed in bytes per secondbytesSent(int): Total bytes sentbytesReceived(int): Total bytes receivedping(int?): Current ping in milliseconds (nullable)connectionDuration(int): Connection duration in milliseconds
Example:
final stats = await singBox.getConnectionStats();
print('Download: ${stats.downloadSpeed} B/s');
print('Upload: ${stats.uploadSpeed} B/s');
print('Ping: ${stats.ping} ms');
watchSingBoxConnectionStats()
Subscribe to connection statistics changes. Returns a Stream that emits statistics updates every second.
Stream<SingBoxConnectionStats> watchSingBoxConnectionStats()
Returns: Stream of connection statistics updates.
Example:
singBox.watchSingBoxConnectionStats().listen((stats) {
// Update UI with real-time statistics
setState(() {
downloadSpeed = stats.downloadSpeed;
uploadSpeed = stats.uploadSpeed;
ping = stats.ping;
});
});
Speed Testing #
testSpeed()
Measure connection speed.
Future<SingBoxSpeedTestResult> testSpeed()
Returns: SingBoxSpeedTestResult with:
downloadSpeed(int): Download speed in bytes per seconduploadSpeed(int): Upload speed in bytes per secondsuccess(bool): Whether test was successfulerrorMessage(String?): Error message if test failed
Example:
final result = await singBox.testSpeed();
if (result.success) {
print('Download: ${result.downloadSpeed} B/s');
print('Upload: ${result.uploadSpeed} B/s');
} else {
print('Error: ${result.errorMessage}');
}
Ping #
pingCurrentServer()
Measure ping to current server.
Future<SingBoxPingResult> pingCurrentServer()
Returns: SingBoxPingResult with:
ping(int): Ping in millisecondssuccess(bool): Whether ping was successfulerrorMessage(String?): Error message if ping failedaddress(String?): Server address
Example:
final result = await singBox.pingCurrentServer();
if (result.success) {
print('Ping: ${result.ping} ms to ${result.address}');
} else {
print('Error: ${result.errorMessage}');
}
Bypass Management #
addAppToBypass(String packageName)
Add app to bypass list (app will not use VPN).
Future<bool> addAppToBypass(String packageName)
Parameters:
packageName(String): App package name (Android) or bundle ID (iOS)
Returns: true if added successfully, false otherwise.
Example:
await singBox.addAppToBypass('com.example.app');
removeAppFromBypass(String packageName)
Remove app from bypass list.
Future<bool> removeAppFromBypass(String packageName)
Example:
await singBox.removeAppFromBypass('com.example.app');
getBypassApps()
Get list of apps in bypass.
Future<List<String>> getBypassApps()
Returns: List of package names/bundle IDs.
Example:
final apps = await singBox.getBypassApps();
print('Bypass apps: $apps');
addDomainToBypass(String domain)
Add domain/site to bypass list (domain will not use VPN).
Future<bool> addDomainToBypass(String domain)
Parameters:
domain(String): Domain name or IP address
Example:
await singBox.addDomainToBypass('example.com');
removeDomainFromBypass(String domain)
Remove domain from bypass list.
Future<bool> removeDomainFromBypass(String domain)
getBypassDomains()
Get list of domains in bypass.
Future<List<String>> getBypassDomains()
Example:
final domains = await singBox.getBypassDomains();
addSubnetToBypass(String subnet)
Add subnet to bypass list (subnet will not use VPN).
Future<bool> addSubnetToBypass(String subnet)
Parameters:
subnet(String): Subnet in CIDR notation (e.g., "192.168.1.0/24", "10.0.0.0/8")
Example:
await singBox.addSubnetToBypass('192.168.1.0/24');
await singBox.addSubnetToBypass('10.0.0.0/8');
removeSubnetFromBypass(String subnet)
Remove subnet from bypass list.
Future<bool> removeSubnetFromBypass(String subnet)
getBypassSubnets()
Get list of subnets in bypass.
Future<List<String>> getBypassSubnets()
Example:
final subnets = await singBox.getBypassSubnets();
DNS Servers Management #
addDnsServer(String dnsServer)
Add DNS server.
Future<bool> addDnsServer(String dnsServer)
Parameters:
dnsServer(String): DNS server IP address (e.g., "8.8.8.8", "1.1.1.1")
Example:
await singBox.addDnsServer('8.8.8.8'); // Google DNS
await singBox.addDnsServer('1.1.1.1'); // Cloudflare DNS
removeDnsServer(String dnsServer)
Remove DNS server.
Future<bool> removeDnsServer(String dnsServer)
getDnsServers()
Get list of DNS servers.
Future<List<String>> getDnsServers()
Example:
final dnsServers = await singBox.getDnsServers();
setDnsServers(List<String> dnsServers)
Set DNS servers (replaces all existing DNS servers).
Future<bool> setDnsServers(List<String> dnsServers)
Parameters:
dnsServers(List
Example:
await singBox.setDnsServers(['8.8.8.8', '8.8.4.4']);
Server Switching #
switchServer(String config)
Switch current server. Stops current connection, changes configuration and connects to new server.
Future<bool> switchServer(String config)
Parameters:
config(String): JSON string with new server configuration
Example:
final newConfig = '{"outbounds": [{"type": "vless", ...}]}';
final switched = await singBox.switchServer(newConfig);
Settings Management #
saveSettings(SingBoxSettings settings)
Save settings.
Future<bool> saveSettings(SingBoxSettings settings)
Parameters:
settings(SingBoxSettings): Settings object to save
Example:
final settings = SingBoxSettings(
autoConnectOnStart: true,
autoReconnectOnDisconnect: true,
killSwitch: true,
);
await singBox.saveSettings(settings);
loadSettings()
Load settings.
Future<SingBoxSettings> loadSettings()
Returns: SingBoxSettings object.
Example:
final settings = await singBox.loadSettings();
getSettings()
Get current settings.
Future<SingBoxSettings> getSettings()
Example:
final settings = await singBox.getSettings();
updateSetting(String key, dynamic value)
Update individual setting parameter.
Future<bool> updateSetting(String key, dynamic value)
Parameters:
key(String): Parameter key (autoConnectOnStart,autoReconnectOnDisconnect,killSwitch)value(dynamic): Parameter value
Example:
await singBox.updateSetting('autoConnectOnStart', true);
await singBox.updateSetting('killSwitch', false);
Settings Keys:
autoConnectOnStart(bool): Automatically connect on application startautoReconnectOnDisconnect(bool): Automatically reconnect on connection losskillSwitch(bool): Block all internet when VPN is disconnected
Server Configurations Management #
addServerConfig(SingBoxServerConfig config)
Add server configuration.
Future<bool> addServerConfig(SingBoxServerConfig config)
Parameters:
config(SingBoxServerConfig): Server configuration object
Example:
final config = SingBoxServerConfig(
id: 'server1-vmess',
name: 'Server 1 - VMESS',
config: '{"outbounds": [{"type": "vmess", ...}]}',
protocol: 'vmess',
server: 'server1.com',
port: 443,
);
await singBox.addServerConfig(config);
removeServerConfig(String configId)
Remove server configuration.
Future<bool> removeServerConfig(String configId)
Parameters:
configId(String): Configuration identifier
Example:
await singBox.removeServerConfig('server1-vmess');
updateServerConfig(SingBoxServerConfig config)
Update server configuration.
Future<bool> updateServerConfig(SingBoxServerConfig config)
Example:
final updatedConfig = config.copyWith(name: 'Updated Name');
await singBox.updateServerConfig(updatedConfig);
getServerConfigs()
Get all server configurations.
Future<List<SingBoxServerConfig>> getServerConfigs()
Returns: List of server configurations.
Example:
final configs = await singBox.getServerConfigs();
for (final config in configs) {
print('${config.name} (${config.protocol})');
}
getServerConfig(String configId)
Get server configuration by ID.
Future<SingBoxServerConfig?> getServerConfig(String configId)
Returns: Server configuration or null if not found.
Example:
final config = await singBox.getServerConfig('server1-vmess');
if (config != null) {
print('Found: ${config.name}');
}
setActiveServerConfig(String configId)
Set active server configuration.
Future<bool> setActiveServerConfig(String configId)
Parameters:
configId(String): Configuration identifier (can benullto reset)
Example:
await singBox.setActiveServerConfig('server1-vmess');
getActiveSingBoxServerConfig()
Get active server configuration.
Future<SingBoxServerConfig?> getActiveSingBoxServerConfig()
Returns: Active server configuration or null if none is set.
Example:
final activeConfig = await singBox.getActiveSingBoxServerConfig();
if (activeConfig != null) {
print('Active: ${activeConfig.name}');
}
Blocked Apps and Domains #
addBlockedApp(String packageName)
Add app to blocked list (app will not use VPN).
Future<bool> addBlockedApp(String packageName)
Parameters:
packageName(String): App package name (Android) or bundle ID (iOS)
Example:
await singBox.addBlockedApp('com.banking.app');
removeBlockedApp(String packageName)
Remove app from blocked list.
Future<bool> removeBlockedApp(String packageName)
getBlockedApps()
Get list of blocked apps.
Future<List<String>> getBlockedApps()
Example:
final blockedApps = await singBox.getBlockedApps();
addBlockedDomain(String domain)
Add domain/site to blocked list (domain will not use VPN).
Future<bool> addBlockedDomain(String domain)
Parameters:
domain(String): Domain name or IP address
Example:
await singBox.addBlockedDomain('malicious-site.com');
removeBlockedDomain(String domain)
Remove domain from blocked list.
Future<bool> removeBlockedDomain(String domain)
getBlockedDomains()
Get list of blocked domains.
Future<List<String>> getBlockedDomains()
Example:
final blockedDomains = await singBox.getBlockedDomains();
Notifications #
watchNotifications()
Subscribe to notifications from sing-box. Returns a Stream that emits notification updates.
Stream<SingBoxNotification> watchNotifications()
Returns: Stream of notification updates.
Notification Properties:
identifier(String): Notification identifiertypeName(String): Notification type nametypeId(int): Notification type IDtitle(String): Notification titlesubtitle(String): Notification subtitlebody(String): Notification bodyopenUrl(String?): URL to open when notification is tapped
Example:
singBox.watchNotifications().listen((notification) {
print('Notification: ${notification.title}');
print('Body: ${notification.body}');
if (notification.openUrl != null) {
// Open URL
}
});
Observer Pattern #
setObserver(SingBoxObserver observer)
Set observer for logging events.
void setObserver(SingBoxObserver observer)
Example with Talker:
import 'package:talker/talker.dart';
import 'package:sing_box/sing_box.dart';
import 'package:sing_box/src/observer/talker_observer.dart';
final talker = Talker();
final observer = SingBoxTalkerObserver(talker);
SingBox.instance.setObserver(observer);
Custom Observer:
import 'package:sing_box/sing_box.dart';
import 'package:sing_box/src/observer/sing_box_observer.dart';
class MyObserver implements SingBoxObserver {
@override
void info(String message, [Map<String, dynamic>? data]) {
print('INFO: $message');
}
@override
void warning(String message, [Map<String, dynamic>? data]) {
print('WARNING: $message');
}
@override
void error(String message, [Object? error, StackTrace? stackTrace]) {
print('ERROR: $message');
}
@override
void debug(String message, [Map<String, dynamic>? data]) {
print('DEBUG: $message');
}
// Implement remaining methods...
}
SingBox.instance.setObserver(MyObserver());
Complete Example #
import 'package:flutter/material.dart';
import 'package:sing_box/sing_box.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize plugin
await SingBox.instance.initialize();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final singBox = SingBox.instance;
SingBoxConnectionStatus _status = SingBoxConnectionStatus.disconnected;
SingBoxConnectionStats? _stats;
@override
void initState() {
super.initState();
// Listen to status changes
singBox.watchSingBoxConnectionStatus().listen((status) {
setState(() {
_status = status;
});
});
// Listen to statistics
singBox.watchSingBoxConnectionStats().listen((stats) {
setState(() {
_stats = stats;
});
});
}
Future<void> _connect() async {
final config = '''
{
"outbounds": [
{
"type": "vmess",
"tag": "proxy",
"server": "example.com",
"server_port": 443,
"uuid": "your-uuid-here",
"security": "auto"
}
]
}
''';
await singBox.connect(config);
}
Future<void> _disconnect() async {
await singBox.disconnect();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Sing-Box Example')),
body: Column(
children: [
Text('Status: ${_status.name}'),
if (_stats != null) ...[
Text('Download: ${_stats!.downloadSpeed} B/s'),
Text('Upload: ${_stats!.uploadSpeed} B/s'),
Text('Ping: ${_stats!.ping} ms'),
],
ElevatedButton(
onPressed: _status == SingBoxConnectionStatus.connected
? _disconnect
: _connect,
child: Text(_status == SingBoxConnectionStatus.connected
? 'Disconnect'
: 'Connect'),
),
],
),
),
);
}
}
Running the Example #
Steps #
-
Clone the repository:
git clone https://github.com/qusadprod/sing_box.git cd sing_box -
Install dependencies:
flutter pub get -
Run on Android:
flutter runOr specify device:
flutter devices flutter run -d <device-id> -
Run on iOS:
flutter runNote: For iOS, you may need to:
- Open
ios/Runner.xcworkspacein Xcode - Configure signing and capabilities
- Build and run from Xcode if needed
- Open
Building for Release #
Android Release Build
flutter build apk --release
# or
flutter build appbundle --release
iOS Release Build
flutter build ios --release
Then open Xcode and archive the app.
Troubleshooting #
Android Issues #
-
VPN Permission Not Granted:
- The plugin will request VPN permission automatically
- User must grant permission in system settings if denied
-
Service Not Starting:
- Check AndroidManifest.xml permissions
- Ensure VPN permission is granted
iOS Issues #
-
Framework Not Found:
- Make sure you've downloaded
Libbox.xcframeworkfrom GitHub Releases - Verify it's in
ios/Frameworks/Libbox.xcframework - Run the installation script:
curl -L https://raw.githubusercontent.com/qusadprod/sing_box/main/install_ios_framework.sh | bash - See INSTALL_IOS_FRAMEWORK.md for detailed instructions
- Make sure you've downloaded
-
Network Extension Not Working:
- Ensure App Groups are configured
- Check Network Extension capability is enabled
- Verify signing and provisioning profiles
-
Build Errors:
- Run
pod installinios/directory - Clean build:
flutter clean && flutter pub get - Make sure
Libbox.xcframeworkis properly linked in Xcode
- Run
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
Acknowledgments #
This plugin uses the following open-source projects:
sing-box #
- Repository: SagerNet/sing-box
- License: GPL-3.0 (see sing-box LICENSE)
- Usage:
- Android: Uses
libboxlibrary fromexperimental/libboxdirectory (compiled tolibbox.aar) - iOS: Uses
Libbox.xcframeworkcompiled fromexperimental/libboxdirectory
- Android: Uses
- Source Location:
- Android sources:
android/libbox/ - iOS sources:
ios/libbox/
- Android sources:
The libbox library provides the core VPN functionality and protocol implementations for both Android and iOS platforms.
Other Dependencies #
- Flutter SDK: Flutter framework and plugin system
- Kotlin Coroutines: For asynchronous operations on Android
- AndroidX Core: Android support libraries
- Gson: JSON serialization for Android
- plugin_platform_interface: Flutter plugin platform interface
License #
This project is licensed under the MIT License - see the LICENSE file for details.
Note: This plugin uses libbox from SagerNet/sing-box, which is licensed under GPL-3.0. Please refer to the sing-box LICENSE for details.