A Flutter plugin that wraps libXray so you can start, stop, and control Xray-core from your Flutter app. It works on Android, iOS, and macOS.
This plugin does not include any VPN or TUN device logic. It only exposes the Xray engine APIs. If you want to route traffic through a TUN interface, you need to set up the TUN fd yourself and pass it to the plugin.
- Android: uses the libXray AAR built with gomobile
- iOS / macOS: uses the LibXray.xcframework built with gomobile
- Dart API: hides all the base64 encoding and gives you plain typed methods
- Flutter 3.3.0 or newer
- Dart 3.11.1 or newer
- Android minSdk 24
- iOS 15.0 or newer
- macOS 11.0 or newer
Add this to your pubspec.yaml:
dependencies:
flutter_xray: ^0.0.1Then run:
flutter pub getNo extra setup is needed. The AAR is bundled automatically.
No extra setup is needed. The xcframework is bundled automatically. CocoaPods will handle linking.
If you want to build libXray from source (for example to update to a newer upstream commit), run the build script:
python3 scripts/build_and_wire.pyThis script will:
- Build
libXray.aarfor Android - Build
LibXray.xcframeworkfor iOS and macOS - Copy both artifacts into the plugin directories
- Patch the native build files if needed
You need Go, gomobile, Python 3, and Xcode (for Apple builds) installed.
All methods that talk to the Xray engine return a CallResponse<T> object. If success is true, the call worked. If success is false, check error for the message.
final xray = FlutterXray();
// Run from a JSON config string
final response = await xray.runXrayFromJSON(
RunXrayFromJSONRequest(
datDir: appDocumentsPath,
configJSON: jsonString,
),
);
if (response.success) {
print('Xray is running');
}
// Stop it
await xray.stopXray();
// Check if it is running
final running = await xray.getXrayState();If you have a TUN file descriptor from your own VPN service, pass it before starting Xray:
await xray.setTunFd(fd);// Validate a config file
final testResult = await xray.testXray(
RunXrayRequest(datDir: path, configPath: configFilePath),
);
// Measure delay
final pingResult = await xray.ping(
PingRequest(datDir: path, configPath: configFilePath),
);
print('Delay: ${pingResult.data} ms');final stats = await xray.queryStats('tag');// Convert v2rayN / Clash share links to Xray JSON
final json = await xray.convertShareLinksToXrayJson(linksText);
// Convert Xray JSON back to share links
final links = await xray.convertXrayJsonToShareLinks(xrayJsonMap);await xray.countGeoData(
CountGeoDataRequest(datDir: path, name: 'geoip', geoType: 'ip'),
);
final geo = await xray.readGeoFiles(base64EncodedConfig);On iOS and macOS you can init and reset DNS:
await xray.initDns(InitDnsRequest(dns: '1.1.1.1', deviceName: 'eth0'));
await xray.resetDns();On Android you can register dialer and listener controllers. The plugin will ask your Flutter app to protect sockets via a secondary method channel flutter_xray/protect.
await xray.registerDialerController();
await xray.registerListenerController();
await xray.initAndroidDns('1.1.1.1');
await xray.resetAndroidDns();The example/ folder has a small demo. You can paste an Xray JSON config, then tap Start, Stop, Test, or Ping. It also shows the Xray version and running state.
To run it:
cd example
flutter runWe welcome contributions. If you want to help, here is how:
- Fork the repo and create a branch for your changes.
- Make your changes and add tests if possible.
- Run
flutter analyzeandflutter testto make sure everything is clean. - Open a pull request with a clear description of what you changed and why.
Please keep the code style consistent with the rest of the project. If you are adding a new feature, open an issue first so we can discuss it.
If you find a security issue, please do not open a public issue. Send an email to the maintainers instead. See SECURITY.md for details.
This project is licensed under the LGPLv3. See the LICENSE file for details.
This plugin is a wrapper around libXray by the XTLS team, which itself wraps Xray-core. All credit for the underlying engine goes to the XTLS project.