import 'package:flutter/material.dart'; import 'package:file_picker/file_picker.dart'; import 'package:process_run/shell.dart'; import 'dart:io'; import 'dart:convert'; import 'package:path_provider/path_provider.dart'; import 'package:path/path.dart' as path; void main() { runApp(NaiveProxyApp()); } class ServerConfig { final String name; final String configPath; final int? ping; ServerConfig({ required this.name, required this.configPath, this.ping }); } class NaiveProxyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'NaiveProxy Launcher', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.purple), useMaterial3: true, ), home: NaiveProxyHomePage(), ); } } class NaiveProxyHomePage extends StatefulWidget { @override _NaiveProxyHomePageState createState() => _NaiveProxyHomePageState(); } class _NaiveProxyHomePageState extends State { List _servers = []; ServerConfig? _selectedServer; bool _isProxyRunning = false; Process? _naiveProcess; List _logs = []; ScrollController _logController = ScrollController(); @override void initState() { super.initState(); _loadServers(); } Future _loadServers() async { // 获取配置目录 Directory configDir = await _getNaiveFlyConfigDirectory(); // 确保目录存在 if (!configDir.existsSync()) { configDir.createSync(recursive: true); } // 读取所有JSON文件 List files = configDir.listSync() .where((file) => file.path.endsWith('.json')) .toList(); setState(() { _servers = files.map((file) { String name = path.basenameWithoutExtension(file.path); return ServerConfig( name: name, configPath: file.path ); }).toList(); }); } Future _getNaiveFlyConfigDirectory() async { if (Platform.isWindows) { final appDataDir = Platform.environment['APPDATA']; return Directory(path.join(appDataDir!, 'NaiveFly')); } else { // 假设是Unix类系统(Linux/macOS) final homeDir = Platform.environment['HOME']; return Directory(path.join(homeDir!, '.config', 'naivefly')); } } Future _launchNaiveProxy() async { if (_isProxyRunning) { _stopNaiveProxy(); return; } if (_selectedServer == null) { _showErrorDialog('Please select a server'); return; } try { // 检查naiveproxy是否已安装 var whichResult = await Shell().run('which naive'); if (whichResult.isEmpty) { _showErrorDialog('NaiveProxy is not installed'); return; } // 启动naive进程 _naiveProcess = await Process.start('naive', [_selectedServer!.configPath]); setState(() { _isProxyRunning = true; _logs.clear(); _addLog('NaiveProxy started: ${_selectedServer!.name}'); }); // 监听进程输出 _naiveProcess!.stdout.listen((List event) { setState(() { _addLog('Output: ${String.fromCharCodes(event)}'); }); }); // 监听进程错误输出 _naiveProcess!.stderr.listen((List event) { setState(() { _addLog('Message: ${String.fromCharCodes(event)}'); }); }); // 监听进程退出 _naiveProcess!.exitCode.then((int code) { setState(() { _isProxyRunning = false; _addLog('NaiveProxy exited with code: $code'); }); }); } catch (e) { _showErrorDialog('Launch failed: ${e.toString()}'); } } void _stopNaiveProxy() { if (_naiveProcess != null) { _naiveProcess!.kill(); setState(() { _isProxyRunning = false; _addLog('NaiveProxy manually stopped'); }); } } void _addLog(String message) { setState(() { _logs.add(message); // 自动滚动到底部 WidgetsBinding.instance.addPostFrameCallback((_) { if (_logController.hasClients) { _logController.animateTo( _logController.position.maxScrollExtent, duration: Duration(milliseconds: 300), curve: Curves.easeOut, ); } }); }); } void _showErrorDialog(String message) { showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: Text('Error'), content: Text(message), actions: [ TextButton( child: Text('OK'), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('NaiveProxy Launcher'), centerTitle: true, actions: [ IconButton( icon: Icon(Icons.refresh), onPressed: _loadServers, ) ], ), body: Column( children: [ // 服务器列表 Container( height: 200, child: ListView.builder( itemCount: _servers.length, itemBuilder: (context, index) { ServerConfig server = _servers[index]; return ListTile( title: Text(server.name), subtitle: Text(server.configPath), trailing: Text('${server.ping ?? 'N/A'} ms', style: TextStyle(color: Colors.green), ), selected: _selectedServer == server, onTap: () { setState(() { _selectedServer = server; }); }, ); }, ), ), // 启动按钮 Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton.icon( icon: Icon(_isProxyRunning ? Icons.stop : Icons.rocket_launch), label: Text(_isProxyRunning ? 'Stop Proxy' : 'Start NaiveProxy'), onPressed: _launchNaiveProxy, style: ElevatedButton.styleFrom( backgroundColor: _isProxyRunning ? Colors.red : Colors.green, padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), ), ), // 日志输出区域 Expanded( child: Container( margin: EdgeInsets.all(16), decoration: BoxDecoration( border: Border.all(color: Colors.grey), borderRadius: BorderRadius.circular(8), ), child: ListView.builder( controller: _logController, itemCount: _logs.length, itemBuilder: (context, index) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), child: Text( _logs[index], style: TextStyle( fontFamily: 'monospace', fontSize: 12, ), ), ); }, ), ), ), ], ), ); } @override void dispose() { _stopNaiveProxy(); _logController.dispose(); super.dispose(); } }