add some basic funcition
This commit is contained in:
289
lib/main.dart
289
lib/main.dart
@ -2,21 +2,34 @@ 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 NaiveProxyApp extends StatelessWidget {
|
||||
const NaiveProxyApp({super.key});
|
||||
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启动器',
|
||||
title: 'NaiveProxy Launcher',
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.purple),
|
||||
useMaterial3: true,
|
||||
),
|
||||
home: NaiveProxyHomePage(),
|
||||
);
|
||||
@ -24,89 +37,152 @@ class NaiveProxyApp extends StatelessWidget {
|
||||
}
|
||||
|
||||
class NaiveProxyHomePage extends StatefulWidget {
|
||||
const NaiveProxyHomePage({super.key});
|
||||
|
||||
@override
|
||||
_NaiveProxyHomePageState createState() => _NaiveProxyHomePageState();
|
||||
}
|
||||
|
||||
class _NaiveProxyHomePageState extends State<NaiveProxyHomePage> {
|
||||
String? _configFilePath;
|
||||
String _status = '未选择配置文件';
|
||||
List<ServerConfig> _servers = [];
|
||||
ServerConfig? _selectedServer;
|
||||
bool _isProxyRunning = false;
|
||||
Process? _naiveProcess;
|
||||
List<String> _logs = [];
|
||||
ScrollController _logController = ScrollController();
|
||||
|
||||
Future<void> _pickConfigFile() async {
|
||||
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
||||
type: FileType.custom,
|
||||
allowedExtensions: ['json'],
|
||||
);
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadServers();
|
||||
}
|
||||
|
||||
if (result != null) {
|
||||
setState(() {
|
||||
_configFilePath = result.files.single.path;
|
||||
_status = '已选择文件: ${_configFilePath!.split('/').last}';
|
||||
});
|
||||
Future<void> _loadServers() async {
|
||||
// 获取配置目录
|
||||
Directory configDir = await _getNaiveFlyConfigDirectory();
|
||||
|
||||
// 确保目录存在
|
||||
if (!configDir.existsSync()) {
|
||||
configDir.createSync(recursive: true);
|
||||
}
|
||||
|
||||
// 读取所有JSON文件
|
||||
List<FileSystemEntity> 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<Directory> _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<void> _launchNaiveProxy() async {
|
||||
if (_configFilePath == null) {
|
||||
_showErrorDialog('请先导入配置文件');
|
||||
if (_isProxyRunning) {
|
||||
_stopNaiveProxy();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_selectedServer == null) {
|
||||
_showErrorDialog('Please select a server');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 检查naiveproxy是否已安装
|
||||
var shell = Shell();
|
||||
var whichResult = await shell.run('which naive');
|
||||
|
||||
var whichResult = await Shell().run('which naive');
|
||||
|
||||
if (whichResult.isEmpty) {
|
||||
_showErrorDialog('NaiveProxy未安装');
|
||||
_showErrorDialog('NaiveProxy is not installed');
|
||||
return;
|
||||
}
|
||||
|
||||
// 尝试执行naive
|
||||
var result = await shell.run('naive $_configFilePath');
|
||||
// 启动naive进程
|
||||
_naiveProcess = await Process.start('naive', [_selectedServer!.configPath]);
|
||||
|
||||
// 显示执行结果
|
||||
_showResultDialog('NaiveProxy已启动', result.outText);
|
||||
setState(() {
|
||||
_isProxyRunning = true;
|
||||
_logs.clear();
|
||||
_addLog('NaiveProxy started: ${_selectedServer!.name}');
|
||||
});
|
||||
|
||||
// 监听进程输出
|
||||
_naiveProcess!.stdout.listen((List<int> event) {
|
||||
setState(() {
|
||||
_addLog('Output: ${String.fromCharCodes(event)}');
|
||||
});
|
||||
});
|
||||
|
||||
// 监听进程错误输出
|
||||
_naiveProcess!.stderr.listen((List<int> event) {
|
||||
setState(() {
|
||||
_addLog('Message: ${String.fromCharCodes(event)}');
|
||||
});
|
||||
});
|
||||
|
||||
// 监听进程退出
|
||||
_naiveProcess!.exitCode.then((int code) {
|
||||
setState(() {
|
||||
_isProxyRunning = false;
|
||||
_addLog('NaiveProxy exited with code: $code');
|
||||
});
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
_showErrorDialog('启动失败:${e.toString()}');
|
||||
_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('错误'),
|
||||
title: Text('Error'),
|
||||
content: Text(message),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: Text('确定'),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _showResultDialog(String title, String message) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(title),
|
||||
content: SingleChildScrollView(
|
||||
child: Text(message),
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: Text('确定'),
|
||||
child: Text('OK'),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
@ -121,43 +197,90 @@ class _NaiveProxyHomePageState extends State<NaiveProxyHomePage> {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('NaiveProxy启动器'),
|
||||
title: Text('NaiveProxy Launcher'),
|
||||
centerTitle: true,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.refresh),
|
||||
onPressed: _loadServers,
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
width: 300,
|
||||
child: Text(
|
||||
_status,
|
||||
style: TextStyle(fontSize: 16),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
// 服务器列表
|
||||
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;
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
ElevatedButton.icon(
|
||||
icon: Icon(Icons.file_upload),
|
||||
label: Text('导入配置文件'),
|
||||
onPressed: _pickConfigFile,
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
ElevatedButton.icon(
|
||||
icon: Icon(Icons.rocket_launch),
|
||||
label: Text('启动NaiveProxy'),
|
||||
),
|
||||
|
||||
// 启动按钮
|
||||
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: Colors.green,
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user