设置代理忽略域名

This commit is contained in:
wanghongen
2023-09-30 20:10:31 +08:00
parent ab12f6e4bc
commit 2a7d5e6ecd
11 changed files with 434 additions and 213 deletions

View File

@@ -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**
重现行为的步骤:
重现行为的步骤: 如具体应用抓包失败,请说明软件名称以及具体操作页面
**屏幕截图*

View File

@@ -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(),

View File

@@ -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);
}
/// 重启代理服务

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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,

View File

@@ -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("主题")),
);
}
}

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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(

View 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),
));
}
}