mirror of
https://github.com/wanghongenpin/proxypin.git
synced 2026-04-30 23:19:46 +08:00
设置代理忽略域名
This commit is contained in:
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create reports to help us improve
|
||||
title: "[系统]xxx"
|
||||
title: "[Windows、Mac、Android]xxx"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
@@ -10,6 +10,6 @@ assignees: ''
|
||||
**描述错误**
|
||||
|
||||
**To Reproduce**
|
||||
重现行为的步骤:
|
||||
重现行为的步骤: 如具体应用抓包失败,请说明软件名称以及具体操作页面
|
||||
|
||||
**屏幕截图*
|
||||
|
||||
@@ -21,10 +21,12 @@ import 'package:network_proxy/network/host_port.dart';
|
||||
import 'package:network_proxy/network/util/host_filter.dart';
|
||||
import 'package:network_proxy/network/util/logger.dart';
|
||||
import 'package:network_proxy/network/util/request_rewrite.dart';
|
||||
import 'package:network_proxy/network/util/system_proxy.dart';
|
||||
import 'package:network_proxy/utils/platform.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class Configuration {
|
||||
///代理相关配置
|
||||
int port = 9099;
|
||||
|
||||
//是否启用https抓包
|
||||
@@ -33,6 +35,9 @@ class Configuration {
|
||||
//是否设置系统代理
|
||||
bool enableSystemProxy = true;
|
||||
|
||||
//代理忽略域名
|
||||
String proxyPassDomains = SystemProxy.proxyPassDomains;
|
||||
|
||||
//是否显示更新内容公告
|
||||
bool upgradeNoticeV3 = true;
|
||||
|
||||
@@ -69,6 +74,7 @@ class Configuration {
|
||||
}
|
||||
|
||||
String? userHome;
|
||||
|
||||
Future<File> homeDir() async {
|
||||
if (userHome != null) {
|
||||
return File("${userHome!}${Platform.pathSeparator}.proxypin");
|
||||
@@ -117,6 +123,7 @@ class Configuration {
|
||||
port = config['port'] ?? port;
|
||||
enableSsl = config['enableSsl'] == true;
|
||||
enableSystemProxy = config['enableSystemProxy'] ?? (config['enableDesktop'] ?? true);
|
||||
proxyPassDomains = config['proxyPassDomains'] ?? SystemProxy.proxyPassDomains;
|
||||
upgradeNoticeV3 = config['upgradeNoticeV3'] ?? true;
|
||||
if (config['externalProxy'] != null) {
|
||||
externalProxy = ProxyInfo.fromJson(config['externalProxy']);
|
||||
@@ -162,6 +169,7 @@ class Configuration {
|
||||
'port': port,
|
||||
'enableSsl': enableSsl,
|
||||
'enableSystemProxy': enableSystemProxy,
|
||||
'proxyPassDomains': proxyPassDomains,
|
||||
'externalProxy': externalProxy?.toJson(),
|
||||
'appWhitelist': appWhitelist,
|
||||
'whitelist': HostFilter.whitelist.toJson(),
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:network_proxy/network/bin/configuration.dart';
|
||||
import 'package:network_proxy/network/channel.dart';
|
||||
@@ -59,9 +58,7 @@ class ProxyServer {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Platform.isMacOS) {
|
||||
SystemProxy.setSslProxyEnableMacOS(enableSsl, port);
|
||||
}
|
||||
SystemProxy.setSslProxyEnable(enableSsl, port);
|
||||
}
|
||||
|
||||
/// 启动代理服务
|
||||
@@ -100,11 +97,11 @@ class ProxyServer {
|
||||
setSystemProxyEnable(bool enable) async {
|
||||
//关闭系统代理 恢复成外部代理地址
|
||||
if (!enable && configuration.externalProxy?.enabled == true) {
|
||||
await SystemProxy.setSystemProxy(configuration.externalProxy!.port!, enableSsl);
|
||||
await SystemProxy.setSystemProxy(configuration.externalProxy!.port!, enableSsl, configuration.proxyPassDomains);
|
||||
return;
|
||||
}
|
||||
|
||||
await SystemProxy.setSystemProxyEnable(port, enable, enableSsl);
|
||||
await SystemProxy.setSystemProxyEnable(port, enable, enableSsl, passDomains: configuration.proxyPassDomains);
|
||||
}
|
||||
|
||||
/// 重启代理服务
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:network_proxy/network/host_port.dart';
|
||||
@@ -8,84 +24,103 @@ import 'package:proxy_manager/proxy_manager.dart';
|
||||
/// @author wanghongen
|
||||
/// 2023/7/26
|
||||
class SystemProxy {
|
||||
static String? _hardwarePort;
|
||||
static SystemProxy? _instance;
|
||||
|
||||
///单例
|
||||
static SystemProxy get instance {
|
||||
if (_instance == null) {
|
||||
if (Platform.isMacOS) {
|
||||
_instance = MacSystemProxy();
|
||||
} else if (Platform.isWindows) {
|
||||
_instance = WindowsSystemProxy();
|
||||
} else if (Platform.isLinux) {
|
||||
_instance = LinuxSystemProxy();
|
||||
} else {
|
||||
_instance = SystemProxy();
|
||||
}
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
///获取代理忽略地址
|
||||
static String get proxyPassDomains {
|
||||
if (Platform.isMacOS) {
|
||||
return '192.168.0.0/16;10.0.0.0/8;172.16.0.0/12;127.0.0.1;localhost;*.local;timestamp.apple.com';
|
||||
}
|
||||
if (Platform.isWindows) {
|
||||
return '192.168.0.*;10.0.0.*;172.16.0.*;127.0.0.1;localhost;*.local;<local>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
///获取系统代理
|
||||
static Future<ProxyInfo?> getSystemProxy(ProxyTypes types) async {
|
||||
if (Platform.isWindows) {
|
||||
return await _getSystemProxyWindows();
|
||||
} else if (Platform.isMacOS) {
|
||||
return await _getSystemProxyMacOS(types);
|
||||
} else if (Platform.isLinux) {
|
||||
return await _getLinuxProxyServer(types);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return instance._getSystemProxy(types);
|
||||
}
|
||||
|
||||
/// 设置系统代理
|
||||
static Future<void> setSystemProxy(int port, bool sslSetting) async {
|
||||
if (Platform.isMacOS) {
|
||||
await _setProxyServerMacOS(port, sslSetting);
|
||||
} else if (Platform.isWindows) {
|
||||
await _setProxyServerWindows(port);
|
||||
} else {
|
||||
ProxyManager manager = ProxyManager();
|
||||
manager.setAsSystemProxy(ProxyTypes.http, "127.0.0.1", port);
|
||||
if (sslSetting) {
|
||||
await manager.setAsSystemProxy(ProxyTypes.https, "127.0.0.1", port);
|
||||
}
|
||||
}
|
||||
///设置系统代理
|
||||
static Future<void> setSystemProxy(int port, bool sslSetting, String proxyPassDomains) async {
|
||||
instance._setSystemProxy(port, sslSetting, proxyPassDomains);
|
||||
}
|
||||
|
||||
///设置Https代理启用状态
|
||||
static void setSslProxyEnable(bool proxyEnable, port) {
|
||||
instance._setSslProxyEnable(proxyEnable, port);
|
||||
}
|
||||
|
||||
/// 设置系统代理
|
||||
/// @param sslSetting 是否设置https代理只在mac中有效
|
||||
static Future<void> setSystemProxyEnable(int port, bool enable, bool sslSetting) async {
|
||||
static Future<void> setSystemProxyEnable(int port, bool enable, bool sslSetting,
|
||||
{required String passDomains}) async {
|
||||
//启用系统代理
|
||||
if (enable) {
|
||||
await setSystemProxy(port, sslSetting);
|
||||
await setSystemProxy(port, sslSetting, passDomains);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Platform.isMacOS) {
|
||||
await setProxyEnableMacOS(enable, sslSetting);
|
||||
} else if (Platform.isWindows) {
|
||||
await setProxyEnableWindows(enable);
|
||||
} else {
|
||||
ProxyManager manager = ProxyManager();
|
||||
await manager.cleanSystemProxy();
|
||||
}
|
||||
instance._setProxyEnable(enable, sslSetting);
|
||||
}
|
||||
|
||||
static Future<bool> _setProxyServerMacOS(int port, bool sslSetting) async {
|
||||
_hardwarePort = await hardwarePort();
|
||||
var results = await Process.run('bash', [
|
||||
'-c',
|
||||
_concatCommands([
|
||||
'networksetup -setwebproxy $_hardwarePort 127.0.0.1 $port',
|
||||
sslSetting == true ? 'networksetup -setsecurewebproxy $_hardwarePort 127.0.0.1 $port' : '',
|
||||
'networksetup -setproxybypassdomains $_hardwarePort 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12 127.0.0.1 localhost *.local timestamp.apple.com',
|
||||
])
|
||||
]);
|
||||
print('set proxyServer, name: $_hardwarePort, exitCode: ${results.exitCode}, stdout: ${results.stdout}');
|
||||
return results.exitCode == 0;
|
||||
///设置代理忽略地址
|
||||
static Future<void> setProxyPassDomains(String proxyPassDomains) async {
|
||||
instance._setProxyPassDomains(proxyPassDomains);
|
||||
}
|
||||
|
||||
static Future<bool> getProxyEnable() async {
|
||||
_hardwarePort ??= await hardwarePort();
|
||||
try {
|
||||
var results = await Process.run('bash', ['-c', 'networksetup -getwebproxy $_hardwarePort']);
|
||||
var proxyEnableLine =
|
||||
(results.stdout as String).split('\n').where((item) => item.contains('Enabled')).first.trim();
|
||||
return proxyEnableLine.endsWith('Yes');
|
||||
} catch (e) {
|
||||
print(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//子类抽象方法
|
||||
|
||||
///获取系统代理
|
||||
static Future<ProxyInfo?> _getSystemProxyMacOS(ProxyTypes proxyTypes) async {
|
||||
Future<ProxyInfo?> _getSystemProxy(ProxyTypes types) async {
|
||||
return null;
|
||||
}
|
||||
|
||||
///设置系统代理
|
||||
Future<void> _setSystemProxy(int port, bool sslSetting, String proxyPassDomains) async {
|
||||
ProxyManager manager = ProxyManager();
|
||||
await manager.setAsSystemProxy(ProxyTypes.https, "127.0.0.1", port);
|
||||
setProxyPassDomains(proxyPassDomains);
|
||||
}
|
||||
|
||||
///设置代理是否启用
|
||||
Future<void> _setProxyEnable(bool proxyEnable, bool sslSetting) async {
|
||||
ProxyManager manager = ProxyManager();
|
||||
await manager.cleanSystemProxy();
|
||||
}
|
||||
|
||||
///设置Https代理启用状态
|
||||
Future<bool> _setSslProxyEnable(bool proxyEnable, int port) async {
|
||||
return false;
|
||||
}
|
||||
|
||||
///设置代理忽略地址
|
||||
Future<void> _setProxyPassDomains(String proxyPassDomains) async {}
|
||||
}
|
||||
|
||||
class MacSystemProxy implements SystemProxy {
|
||||
static String? _hardwarePort;
|
||||
|
||||
///获取系统代理
|
||||
@override
|
||||
Future<ProxyInfo?> _getSystemProxy(ProxyTypes proxyTypes) async {
|
||||
_hardwarePort = await hardwarePort();
|
||||
var result = await Process.run('bash', [
|
||||
'-c',
|
||||
@@ -105,22 +140,25 @@ class SystemProxy {
|
||||
return null;
|
||||
}
|
||||
|
||||
static Future<bool> setProxyEnableMacOS(bool proxyEnable, bool sslSetting) async {
|
||||
var proxyMode = proxyEnable ? 'on' : 'off';
|
||||
_hardwarePort ??= await hardwarePort();
|
||||
print('set proxyEnable: $proxyEnable, name: $_hardwarePort');
|
||||
|
||||
///mac设置代理地址
|
||||
@override
|
||||
Future<bool> _setSystemProxy(int port, bool sslSetting, String proxyPassDomains) async {
|
||||
_hardwarePort = await hardwarePort();
|
||||
var results = await Process.run('bash', [
|
||||
'-c',
|
||||
_concatCommands([
|
||||
'networksetup -setwebproxystate $_hardwarePort $proxyMode',
|
||||
sslSetting ? 'networksetup -setsecurewebproxystate $_hardwarePort $proxyMode' : ''
|
||||
'networksetup -setwebproxy $_hardwarePort 127.0.0.1 $port',
|
||||
sslSetting == true ? 'networksetup -setsecurewebproxy $_hardwarePort 127.0.0.1 $port' : '',
|
||||
'networksetup -setproxybypassdomains $_hardwarePort ${proxyPassDomains.replaceAll(";", " ")}',
|
||||
])
|
||||
]);
|
||||
print('set proxyServer, name: $_hardwarePort, exitCode: ${results.exitCode}, stdout: ${results.stdout}');
|
||||
return results.exitCode == 0;
|
||||
}
|
||||
|
||||
static Future<bool> setSslProxyEnableMacOS(bool proxyEnable, port) async {
|
||||
///设置Https代理
|
||||
@override
|
||||
Future<bool> _setSslProxyEnable(bool proxyEnable, port) async {
|
||||
var name = await hardwarePort();
|
||||
|
||||
var results = await Process.run('bash', [
|
||||
@@ -132,6 +170,7 @@ class SystemProxy {
|
||||
return results.exitCode == 0;
|
||||
}
|
||||
|
||||
///mac获取当前网络名称
|
||||
static Future<String> hardwarePort() async {
|
||||
var name = await networkName();
|
||||
var results = await Process.run('bash', [
|
||||
@@ -141,27 +180,46 @@ class SystemProxy {
|
||||
return results.stdout.toString().split(", ")[0];
|
||||
}
|
||||
|
||||
static Future<void> _setProxyServerWindows(int proxyPort) async {
|
||||
ProxyManager manager = ProxyManager();
|
||||
await manager.setAsSystemProxy(ProxyTypes.https, "127.0.0.1", proxyPort);
|
||||
var results = await _internetSettings('add', [
|
||||
'ProxyOverride',
|
||||
'/t',
|
||||
'REG_SZ',
|
||||
'/d',
|
||||
'192.168.0.*;10.0.0.*;172.16.0.*;127.0.0.1;localhost;*.local;<local>',
|
||||
'/f'
|
||||
]);
|
||||
|
||||
print('set proxyServer $proxyPort, result: $results');
|
||||
///设置代理忽略地址
|
||||
@override
|
||||
Future<void> _setProxyPassDomains(String proxyPassDomains) async {
|
||||
_hardwarePort ??= await hardwarePort();
|
||||
var results = await Process.run(
|
||||
'bash', ['-c', 'networksetup -setproxybypassdomains $_hardwarePort ${proxyPassDomains.replaceAll(";", " ")}']);
|
||||
print('set proxyPassDomains, name: $_hardwarePort, exitCode: ${results.exitCode}, stdout: ${results.stdout}');
|
||||
}
|
||||
|
||||
static Future<void> setProxyEnableWindows(bool proxyEnable) async {
|
||||
///mac设置代理是否启用
|
||||
@override
|
||||
Future<void> _setProxyEnable(bool proxyEnable, bool sslSetting) async {
|
||||
var proxyMode = proxyEnable ? 'on' : 'off';
|
||||
_hardwarePort ??= await hardwarePort();
|
||||
print('set proxyEnable: $proxyEnable, name: $_hardwarePort');
|
||||
|
||||
await Process.run('bash', [
|
||||
'-c',
|
||||
_concatCommands([
|
||||
'networksetup -setwebproxystate $_hardwarePort $proxyMode',
|
||||
sslSetting ? 'networksetup -setsecurewebproxystate $_hardwarePort $proxyMode' : ''
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
static _concatCommands(List<String> commands) {
|
||||
return commands.where((element) => element.isNotEmpty).join(' && ');
|
||||
}
|
||||
}
|
||||
|
||||
class WindowsSystemProxy extends SystemProxy {
|
||||
///设置windows代理是否启用
|
||||
@override
|
||||
Future<void> _setProxyEnable(bool proxyEnable, bool sslSetting) async {
|
||||
await _internetSettings('add', ['ProxyEnable', '/t', 'REG_DWORD', '/f', '/d', proxyEnable ? '1' : '0']);
|
||||
}
|
||||
|
||||
/// 获取系统代理
|
||||
static Future<ProxyInfo?> _getSystemProxyWindows() async {
|
||||
///获取系统代理
|
||||
@override
|
||||
Future<ProxyInfo?> _getSystemProxy(ProxyTypes types) async {
|
||||
var results = await _internetSettings('query', ['ProxyEnable']);
|
||||
|
||||
var proxyEnableLine = results.split('\r\n').where((item) => item.contains('ProxyEnable')).first.trim();
|
||||
@@ -192,6 +250,13 @@ class SystemProxy {
|
||||
});
|
||||
}
|
||||
|
||||
///设置代理忽略地址
|
||||
@override
|
||||
Future<void> _setProxyPassDomains(String proxyPassDomains) async {
|
||||
var results = await _internetSettings('add', ['ProxyOverride', '/t', 'REG_SZ', '/d', proxyPassDomains, '/f']);
|
||||
print('set proxyPassDomains, stdout: $results');
|
||||
}
|
||||
|
||||
static Future<String> _internetSettings(String cmd, List<String> args) async {
|
||||
return Process.run('reg', [
|
||||
cmd,
|
||||
@@ -200,9 +265,12 @@ class SystemProxy {
|
||||
...args,
|
||||
]).then((results) => results.stdout.toString());
|
||||
}
|
||||
}
|
||||
|
||||
static Future<ProxyInfo?> _getLinuxProxyServer(ProxyTypes types) async {
|
||||
///linux 获取代理
|
||||
class LinuxSystemProxy extends SystemProxy {
|
||||
///linux 获取代理
|
||||
@override
|
||||
Future<ProxyInfo?> _getSystemProxy(ProxyTypes types) async {
|
||||
var mode = await Process.run("gsettings", ["get", "org.gnome.system.proxy", "mode"])
|
||||
.then((value) => value.stdout.toString().trim());
|
||||
if (mode.contains("manual")) {
|
||||
@@ -223,10 +291,6 @@ class SystemProxy {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static _concatCommands(List<String> commands) {
|
||||
return commands.where((element) => element.isNotEmpty).join(' && ');
|
||||
}
|
||||
}
|
||||
|
||||
void main() async {
|
||||
|
||||
@@ -23,13 +23,12 @@ class _FilterDialogState extends State<FilterDialog> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
titlePadding: const EdgeInsets.only(left: 20, top: 10, right: 15),
|
||||
contentPadding: const EdgeInsets.only(left: 20, right: 20),
|
||||
scrollable: true,
|
||||
title: const Row(children: [
|
||||
Text("域名过滤", style: TextStyle(fontSize: 18)),
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: CloseButton()))
|
||||
Expanded(child: Align(alignment: Alignment.topRight, child: CloseButton()))
|
||||
]),
|
||||
content: SizedBox(
|
||||
width: 680,
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:network_proxy/network/bin/configuration.dart';
|
||||
import 'package:network_proxy/network/bin/server.dart';
|
||||
import 'package:network_proxy/network/util/system_proxy.dart';
|
||||
import 'package:network_proxy/ui/desktop/toolbar/setting/external_proxy.dart';
|
||||
import 'package:network_proxy/ui/desktop/toolbar/setting/request_rewrite.dart';
|
||||
import 'package:network_proxy/ui/desktop/toolbar/setting/theme.dart';
|
||||
@@ -20,63 +21,62 @@ class Setting extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _SettingState extends State<Setting> {
|
||||
late ValueNotifier<bool> enableDesktopListenable;
|
||||
late Configuration configuration;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
configuration = widget.proxyServer.configuration;
|
||||
enableDesktopListenable = ValueNotifier<bool>(configuration.enableSystemProxy);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
enableDesktopListenable.dispose();
|
||||
super.dispose();
|
||||
Widget item(String text, {VoidCallback? onPressed}) {
|
||||
return MenuItemButton(
|
||||
trailingIcon: const Icon(Icons.arrow_right),
|
||||
onPressed: onPressed,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 10, right: 5),
|
||||
child: Text(text, style: const TextStyle(fontSize: 14))));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PopupMenuButton<String>(
|
||||
tooltip: "设置",
|
||||
icon: const Icon(Icons.settings),
|
||||
surfaceTintColor: Colors.white70,
|
||||
offset: const Offset(10, 30),
|
||||
itemBuilder: (context) {
|
||||
return [
|
||||
PopupMenuItem<String>(
|
||||
child: PortWidget(proxyServer: widget.proxyServer, textStyle: const TextStyle(fontSize: 13))),
|
||||
PopupMenuItem<String>(
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: enableDesktopListenable,
|
||||
builder: (_, val, __) => setSystemProxy(),
|
||||
)),
|
||||
const PopupMenuItem(child: ThemeSetting(dense: true)),
|
||||
menuItem("域名过滤", onTap: hostFilter),
|
||||
menuItem("请求重写", onTap: requestRewrite),
|
||||
menuItem("外部代理设置", onTap: setExternalProxy),
|
||||
menuItem(
|
||||
"Github",
|
||||
onTap: () {
|
||||
launchUrl(Uri.parse("https://github.com/wanghongenpin/network_proxy_flutter"));
|
||||
},
|
||||
)
|
||||
];
|
||||
var surfaceTintColor =
|
||||
Brightness.dark == Theme.of(context).brightness ? null : Theme.of(context).colorScheme.background;
|
||||
return MenuAnchor(
|
||||
style: MenuStyle(surfaceTintColor: MaterialStatePropertyAll(surfaceTintColor)),
|
||||
builder: (context, controller, child) {
|
||||
return IconButton(
|
||||
icon: const Icon(Icons.settings),
|
||||
tooltip: "设置",
|
||||
onPressed: () {
|
||||
if (controller.isOpen) {
|
||||
controller.close();
|
||||
} else {
|
||||
controller.open();
|
||||
}
|
||||
});
|
||||
},
|
||||
menuChildren: [
|
||||
_ProxyMenu(proxyServer: widget.proxyServer),
|
||||
item("域名过滤", onPressed: hostFilter),
|
||||
item("请求重写", onPressed: requestRewrite),
|
||||
const ThemeSetting(),
|
||||
item("外部代理设置", onPressed: setExternalProxy),
|
||||
item("Github", onPressed: () => launchUrl(Uri.parse("https://github.com/wanghongenpin/network_proxy_flutter"))),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
PopupMenuItem<String> menuItem(String title, {GestureTapCallback? onTap}) {
|
||||
return PopupMenuItem<String>(
|
||||
child: ListTile(
|
||||
title: Text(title),
|
||||
dense: true,
|
||||
hoverColor: Colors.transparent,
|
||||
focusColor: Colors.transparent,
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
onTap: onTap,
|
||||
));
|
||||
title: Text(title),
|
||||
dense: true,
|
||||
hoverColor: Colors.transparent,
|
||||
focusColor: Colors.transparent,
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
onTap: onTap,
|
||||
));
|
||||
}
|
||||
|
||||
///设置外部代理地址
|
||||
@@ -89,22 +89,6 @@ class _SettingState extends State<Setting> {
|
||||
});
|
||||
}
|
||||
|
||||
///设置系统代理
|
||||
Widget setSystemProxy() {
|
||||
return SwitchListTile(
|
||||
hoverColor: Colors.transparent,
|
||||
title: const Text("设置为系统代理"),
|
||||
visualDensity: const VisualDensity(horizontal: -4),
|
||||
dense: true,
|
||||
value: configuration.enableSystemProxy,
|
||||
onChanged: (val) {
|
||||
widget.proxyServer.setSystemProxyEnable(val);
|
||||
configuration.enableSystemProxy = val;
|
||||
enableDesktopListenable.value = !enableDesktopListenable.value;
|
||||
configuration.flushConfig();
|
||||
});
|
||||
}
|
||||
|
||||
///请求重写Dialog
|
||||
void requestRewrite() {
|
||||
showDialog(
|
||||
@@ -112,13 +96,12 @@ class _SettingState extends State<Setting> {
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
titlePadding: const EdgeInsets.only(left: 24, top: 10, right: 15),
|
||||
contentPadding: const EdgeInsets.only(left: 24, right: 20),
|
||||
scrollable: true,
|
||||
title: const Row(children: [
|
||||
Text("请求重写", style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500)),
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: CloseButton()))
|
||||
Expanded(child: Align(alignment: Alignment.topRight, child: CloseButton()))
|
||||
]),
|
||||
content: RequestRewrite(configuration: configuration),
|
||||
);
|
||||
@@ -137,6 +120,116 @@ class _SettingState extends State<Setting> {
|
||||
}
|
||||
}
|
||||
|
||||
///代理菜单
|
||||
class _ProxyMenu extends StatefulWidget {
|
||||
final ProxyServer proxyServer;
|
||||
|
||||
const _ProxyMenu({required this.proxyServer});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _ProxyMenuState();
|
||||
}
|
||||
|
||||
class _ProxyMenuState extends State<_ProxyMenu> {
|
||||
var textEditingController = TextEditingController();
|
||||
|
||||
late Configuration configuration;
|
||||
bool changed = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
configuration = widget.proxyServer.configuration;
|
||||
textEditingController.text = configuration.proxyPassDomains;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (configuration.proxyPassDomains != textEditingController.text) {
|
||||
changed = true;
|
||||
configuration.proxyPassDomains = textEditingController.text;
|
||||
SystemProxy.setProxyPassDomains(configuration.proxyPassDomains);
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
configuration.flushConfig();
|
||||
}
|
||||
textEditingController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var surfaceTintColor =
|
||||
Brightness.dark == Theme.of(context).brightness ? null : Theme.of(context).colorScheme.background;
|
||||
|
||||
return SubmenuButton(
|
||||
menuStyle: MenuStyle(
|
||||
surfaceTintColor: MaterialStatePropertyAll(surfaceTintColor),
|
||||
padding: const MaterialStatePropertyAll(EdgeInsets.only(top: 10, bottom: 10)),
|
||||
),
|
||||
menuChildren: [
|
||||
PortWidget(proxyServer: widget.proxyServer, textStyle: const TextStyle(fontSize: 13)),
|
||||
const Divider(thickness: 0.3, height: 8),
|
||||
setSystemProxy(),
|
||||
const Divider(thickness: 0.3, height: 8),
|
||||
const SizedBox(height: 3),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 15),
|
||||
child: Row(children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text("代理忽略域名"),
|
||||
const SizedBox(height: 3),
|
||||
Text("多个使用;分割", style: TextStyle(fontSize: 11, color: Colors.grey.shade600)),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 35),
|
||||
child: TextButton(
|
||||
child: const Text("重置"),
|
||||
onPressed: () {
|
||||
textEditingController.text = SystemProxy.proxyPassDomains;
|
||||
},
|
||||
))
|
||||
])),
|
||||
const SizedBox(height: 5),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 15, right: 5),
|
||||
child: TextField(
|
||||
textInputAction: TextInputAction.done,
|
||||
style: const TextStyle(fontSize: 13),
|
||||
controller: textEditingController,
|
||||
decoration: const InputDecoration(
|
||||
contentPadding: EdgeInsets.all(10),
|
||||
border: OutlineInputBorder(),
|
||||
constraints: BoxConstraints(minWidth: 190, maxWidth: 190)),
|
||||
maxLines: 5,
|
||||
minLines: 1)),
|
||||
const SizedBox(height: 10),
|
||||
],
|
||||
child: const Padding(padding: EdgeInsets.only(left: 10), child: Text("代理")),
|
||||
);
|
||||
}
|
||||
|
||||
///设置系统代理
|
||||
Widget setSystemProxy() {
|
||||
return SwitchListTile(
|
||||
hoverColor: Colors.transparent,
|
||||
title: const Text("设置为系统代理", maxLines: 1),
|
||||
dense: true,
|
||||
value: configuration.enableSystemProxy,
|
||||
onChanged: (val) {
|
||||
widget.proxyServer.setSystemProxyEnable(val);
|
||||
configuration.enableSystemProxy = val;
|
||||
setState(() {
|
||||
changed = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class PortWidget extends StatefulWidget {
|
||||
final ProxyServer proxyServer;
|
||||
final TextStyle? textStyle;
|
||||
@@ -179,6 +272,7 @@ class _PortState extends State<PortWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(children: [
|
||||
const Padding(padding: EdgeInsets.only(left: 15)),
|
||||
Text("端口号:", style: widget.textStyle),
|
||||
SizedBox(
|
||||
width: 80,
|
||||
|
||||
@@ -2,52 +2,63 @@ import 'package:flutter/material.dart';
|
||||
import 'package:network_proxy/main.dart';
|
||||
|
||||
class ThemeSetting extends StatelessWidget {
|
||||
final bool dense;
|
||||
|
||||
const ThemeSetting({Key? key, this.dense = false}) : super(key: key);
|
||||
const ThemeSetting({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PopupMenuButton(
|
||||
tooltip: themeNotifier.value.mode.name,
|
||||
surfaceTintColor: Theme.of(context).colorScheme.onPrimary,
|
||||
offset: const Offset(150, 0),
|
||||
itemBuilder: (BuildContext context) {
|
||||
return [
|
||||
PopupMenuItem(
|
||||
child: Tooltip(
|
||||
preferBelow: false,
|
||||
message: "Material 3是谷歌开源设计系统的最新版本",
|
||||
child: SwitchListTile(
|
||||
value: themeNotifier.value.useMaterial3,
|
||||
onChanged: (bool value) {
|
||||
themeNotifier.value = themeNotifier.value.copy(useMaterial3: value);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
dense: true,
|
||||
title: const Text("Material3"),
|
||||
))),
|
||||
PopupMenuItem(
|
||||
child: const ListTile(trailing: Icon(Icons.cached), dense: true, title: Text("跟随系统")),
|
||||
onTap: () {
|
||||
themeNotifier.value = themeNotifier.value.copy(mode: ThemeMode.system);
|
||||
}),
|
||||
PopupMenuItem(
|
||||
child: const ListTile(trailing: Icon(Icons.nightlight_outlined), dense: true, title: Text("深色")),
|
||||
onTap: () {
|
||||
themeNotifier.value = themeNotifier.value.copy(mode: ThemeMode.dark);
|
||||
}),
|
||||
PopupMenuItem(
|
||||
child: const ListTile(trailing: Icon(Icons.sunny), dense: true, title: Text("浅色")),
|
||||
onTap: () {
|
||||
themeNotifier.value = themeNotifier.value.copy(mode: ThemeMode.light);
|
||||
}),
|
||||
];
|
||||
},
|
||||
child: ListTile(
|
||||
title: const Text("主题"),
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
dense: dense,
|
||||
));
|
||||
var surfaceTintColor =
|
||||
Brightness.dark == Theme.of(context).brightness ? null : Theme.of(context).colorScheme.background;
|
||||
|
||||
return SubmenuButton(
|
||||
menuStyle: MenuStyle(
|
||||
surfaceTintColor: MaterialStatePropertyAll(surfaceTintColor),
|
||||
padding: const MaterialStatePropertyAll(EdgeInsets.only(top: 10, bottom: 10)),
|
||||
),
|
||||
menuChildren: [
|
||||
SizedBox(
|
||||
width: 180,
|
||||
height: 38,
|
||||
child: Tooltip(
|
||||
preferBelow: false,
|
||||
message: "Material 3是谷歌开源设计系统的最新版本",
|
||||
child: SwitchListTile(
|
||||
contentPadding: const EdgeInsets.only(left: 32, right: 5),
|
||||
value: themeNotifier.value.useMaterial3,
|
||||
onChanged: (bool value) {
|
||||
themeNotifier.value = themeNotifier.value.copy(useMaterial3: value);
|
||||
},
|
||||
dense: true,
|
||||
title: const Text("Material3"),
|
||||
))),
|
||||
MenuItemButton(
|
||||
leadingIcon: themeNotifier.value.mode == ThemeMode.system
|
||||
? const Icon(Icons.check, size: 15)
|
||||
: const SizedBox(width: 18),
|
||||
trailingIcon: const Icon(Icons.cached),
|
||||
child: const Text("跟随系统"),
|
||||
onPressed: () {
|
||||
themeNotifier.value = themeNotifier.value.copy(mode: ThemeMode.system);
|
||||
}),
|
||||
MenuItemButton(
|
||||
leadingIcon: themeNotifier.value.mode == ThemeMode.dark
|
||||
? const Icon(Icons.check, size: 15)
|
||||
: const SizedBox(width: 15),
|
||||
trailingIcon: const Icon(Icons.nightlight_outlined),
|
||||
child: const Text("深色"),
|
||||
onPressed: () {
|
||||
themeNotifier.value = themeNotifier.value.copy(mode: ThemeMode.dark);
|
||||
}),
|
||||
MenuItemButton(
|
||||
leadingIcon: themeNotifier.value.mode == ThemeMode.light
|
||||
? const Icon(Icons.check, size: 15)
|
||||
: const SizedBox(width: 15),
|
||||
trailingIcon: const Icon(Icons.sunny),
|
||||
child: const Text("浅色"),
|
||||
onPressed: () {
|
||||
themeNotifier.value = themeNotifier.value.copy(mode: ThemeMode.light);
|
||||
}),
|
||||
],
|
||||
child: const Padding(padding: EdgeInsets.only(left: 10), child: Text("主题")),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,12 @@ class SslWidget extends StatefulWidget {
|
||||
class _SslState extends State<SslWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var surfaceTintColor =
|
||||
Brightness.dark == Theme.of(context).brightness ? null : Theme.of(context).colorScheme.background;
|
||||
|
||||
return PopupMenuButton<String>(
|
||||
icon: Icon(Icons.https, color: widget.proxyServer.enableSsl ? null : Colors.red),
|
||||
surfaceTintColor: Colors.white70,
|
||||
surfaceTintColor: surfaceTintColor,
|
||||
tooltip: "HTTPS代理",
|
||||
offset: const Offset(10, 30),
|
||||
itemBuilder: (context) {
|
||||
|
||||
@@ -60,12 +60,7 @@ class _ToolbarState extends State<Toolbar> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
// decoration: BoxDecoration(
|
||||
// border: Border(
|
||||
// bottom: BorderSide(color: Theme.of(context).dividerColor, width: 0.1),
|
||||
// )),
|
||||
child: Row(
|
||||
return Row(
|
||||
children: [
|
||||
Padding(padding: EdgeInsets.only(left: Platform.isMacOS ? 80 : 30)),
|
||||
SocketLaunch(proxyServer: widget.proxyServer),
|
||||
@@ -103,7 +98,7 @@ class _ToolbarState extends State<Toolbar> {
|
||||
)), //右对齐
|
||||
const Padding(padding: EdgeInsets.only(left: 30)),
|
||||
],
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
phoneConnect(List<String> hosts, int port) {
|
||||
|
||||
@@ -9,7 +9,6 @@ import 'package:network_proxy/network/http_client.dart';
|
||||
import 'package:network_proxy/network/util/host_filter.dart';
|
||||
import 'package:network_proxy/ui/component/toolbox.dart';
|
||||
import 'package:network_proxy/ui/desktop/toolbar/setting/setting.dart';
|
||||
import 'package:network_proxy/ui/desktop/toolbar/setting/theme.dart';
|
||||
import 'package:network_proxy/ui/mobile/connect_remote.dart';
|
||||
import 'package:network_proxy/ui/mobile/request/favorite.dart';
|
||||
import 'package:network_proxy/ui/mobile/request/history.dart';
|
||||
@@ -18,6 +17,7 @@ import 'package:network_proxy/ui/mobile/setting/app_whitelist.dart';
|
||||
import 'package:network_proxy/ui/mobile/setting/filter.dart';
|
||||
import 'package:network_proxy/ui/mobile/setting/request_rewrite.dart';
|
||||
import 'package:network_proxy/ui/mobile/setting/ssl.dart';
|
||||
import 'package:network_proxy/ui/mobile/setting/theme.dart';
|
||||
import 'package:network_proxy/utils/ip.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
import 'package:qrscan/qrscan.dart' as scanner;
|
||||
@@ -52,12 +52,12 @@ class DrawerWidget extends StatelessWidget {
|
||||
onTap: () => navigator(context, MobileHistory(proxyServer: proxyServer, requestStateKey: requestStateKey)),
|
||||
),
|
||||
const Divider(thickness: 0.3),
|
||||
Padding(padding: const EdgeInsets.only(left: 15), child: PortWidget(proxyServer: proxyServer)),
|
||||
PortWidget(proxyServer: proxyServer),
|
||||
ListTile(
|
||||
title: const Text("HTTPS抓包"),
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
onTap: () => navigator(context, MobileSslWidget(proxyServer: proxyServer))),
|
||||
const ThemeSetting(),
|
||||
const MobileThemeSetting(),
|
||||
Platform.isIOS
|
||||
? const SizedBox()
|
||||
: ListTile(
|
||||
|
||||
50
lib/ui/mobile/setting/theme.dart
Normal file
50
lib/ui/mobile/setting/theme.dart
Normal file
@@ -0,0 +1,50 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:network_proxy/main.dart';
|
||||
|
||||
class MobileThemeSetting extends StatelessWidget {
|
||||
const MobileThemeSetting({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PopupMenuButton(
|
||||
tooltip: themeNotifier.value.mode.name,
|
||||
surfaceTintColor: Theme.of(context).colorScheme.onPrimary,
|
||||
offset: const Offset(150, 0),
|
||||
itemBuilder: (BuildContext context) {
|
||||
return [
|
||||
PopupMenuItem(
|
||||
child: Tooltip(
|
||||
preferBelow: false,
|
||||
message: "Material 3是谷歌开源设计系统的最新版本",
|
||||
child: SwitchListTile(
|
||||
value: themeNotifier.value.useMaterial3,
|
||||
onChanged: (bool value) {
|
||||
themeNotifier.value = themeNotifier.value.copy(useMaterial3: value);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
dense: true,
|
||||
title: const Text("Material3"),
|
||||
))),
|
||||
PopupMenuItem(
|
||||
child: const ListTile(trailing: Icon(Icons.cached), dense: true, title: Text("跟随系统")),
|
||||
onTap: () {
|
||||
themeNotifier.value = themeNotifier.value.copy(mode: ThemeMode.system);
|
||||
}),
|
||||
PopupMenuItem(
|
||||
child: const ListTile(trailing: Icon(Icons.nightlight_outlined), dense: true, title: Text("深色")),
|
||||
onTap: () {
|
||||
themeNotifier.value = themeNotifier.value.copy(mode: ThemeMode.dark);
|
||||
}),
|
||||
PopupMenuItem(
|
||||
child: const ListTile(trailing: Icon(Icons.sunny), dense: true, title: Text("浅色")),
|
||||
onTap: () {
|
||||
themeNotifier.value = themeNotifier.value.copy(mode: ThemeMode.light);
|
||||
}),
|
||||
];
|
||||
},
|
||||
child: const ListTile(
|
||||
title: Text("主题"),
|
||||
trailing: Icon(Icons.arrow_right),
|
||||
));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user