Files
vpn/lib/main_view.dart
2026-04-04 19:17:35 +03:00

145 lines
3.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_v2ray_client/flutter_v2ray.dart';
import 'package:ionicons/ionicons.dart';
class MainView extends StatefulWidget {
const MainView({super.key});
@override
State<MainView> createState() => _MainViewState();
}
class _MainViewState extends State<MainView> {
bool _toogleFlag = false;
bool _isProcessing = false;
String _connectionState = 'DISCONNECTED';
String _config = "";
late final V2ray _v2ray = V2ray(
onStatusChanged: (status) {
if (!mounted) return;
setState(() {
_connectionState = status.state;
_toogleFlag = status.state == 'CONNECTED';
_isProcessing = false;
});
},
);
@override
void initState() {
super.initState();
_initializeV2Ray();
}
Future<void> _initializeV2Ray() async {
try {
await _v2ray.initialize();
} catch (e) {
_showMessage('Init error: $e');
}
}
void _showMessage(String message) {
if (!mounted) return;
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(message)));
}
Future<void> _setConfig() async {
try {
final clipboardData = await Clipboard.getData(Clipboard.kTextPlain);
final text = clipboardData?.text?.trim() ?? "";
if (text.isEmpty) {
_showMessage('Clipboard is empty');
return;
}
setState(() {
_config = text;
});
_showMessage('Config link loaded from clipboard');
} catch (e) {
_showMessage('Clipboard read error: $e');
}
}
Future<void> _toogleConfig() async {
if (_isProcessing) return;
setState(() {
_isProcessing = true;
});
try {
if (_toogleFlag) {
await _v2ray.stopV2Ray();
if (mounted) {
setState(() {
_connectionState = 'DISCONNECTED';
_toogleFlag = false;
});
}
return;
}
final link = _config.trim();
if (link.isEmpty) {
_showMessage('Config link is empty');
return;
}
final parsed = V2ray.parseFromURL(link);
final granted = await _v2ray.requestPermission();
if (!granted) {
_showMessage('VPN permission denied');
return;
}
await _v2ray.startV2Ray(
remark: parsed.remark.isEmpty ? 'VPN' : parsed.remark,
config: parsed.getFullConfiguration(),
proxyOnly: false,
);
if (mounted) {
setState(() {
_connectionState = 'CONNECTING';
});
}
} catch (e) {
_showMessage('Error: $e');
} finally {
if (mounted) {
setState(() {
_isProcessing = false;
});
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Status: $_connectionState'),
const SizedBox(height: 12),
OutlinedButton.icon(
onPressed: _isProcessing ? null : _setConfig,
icon: _isProcessing
? const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(strokeWidth: 2),
)
: Icon(_toogleFlag ? Ionicons.stop : Ionicons.play),
label: Text(_toogleFlag ? "Stop" : "Start"),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _toogleConfig,
child: Icon(Ionicons.create),
),
);
}
}