diff --git a/lib/ui/mobile/menu.dart b/lib/ui/mobile/menu.dart index 9adac2c..703585e 100644 --- a/lib/ui/mobile/menu.dart +++ b/lib/ui/mobile/menu.dart @@ -8,13 +8,13 @@ import 'package:network_proxy/network/bin/server.dart'; 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/mobile/connect_remote.dart'; import 'package:network_proxy/ui/mobile/request/favorite.dart'; import 'package:network_proxy/ui/mobile/request/history.dart'; import 'package:network_proxy/ui/mobile/request/list.dart'; 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/proxy.dart'; import 'package:network_proxy/ui/mobile/setting/request_rewrite.dart'; import 'package:network_proxy/ui/mobile/setting/script.dart'; import 'package:network_proxy/ui/mobile/setting/ssl.dart'; @@ -29,7 +29,7 @@ class DrawerWidget extends StatelessWidget { final ProxyServer proxyServer; final GlobalKey requestStateKey; - const DrawerWidget({Key? key, required this.proxyServer, required this.requestStateKey}) : super(key: key); + const DrawerWidget({super.key, required this.proxyServer, required this.requestStateKey}); @override Widget build(BuildContext context) { @@ -53,7 +53,10 @@ class DrawerWidget extends StatelessWidget { onTap: () => navigator(context, MobileHistory(proxyServer: proxyServer, requestStateKey: requestStateKey)), ), const Divider(thickness: 0.3), - PortWidget(proxyServer: proxyServer), + ListTile( + title: const Text("代理"), + trailing: const Icon(Icons.arrow_right), + onTap: () => navigator(context, ProxySetting(proxyServer: proxyServer))), ListTile( title: const Text("HTTPS抓包"), trailing: const Icon(Icons.arrow_right), diff --git a/lib/ui/mobile/setting/proxy.dart b/lib/ui/mobile/setting/proxy.dart new file mode 100644 index 0000000..3c967a4 --- /dev/null +++ b/lib/ui/mobile/setting/proxy.dart @@ -0,0 +1,165 @@ +import 'dart:io'; + +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/host_port.dart'; +import 'package:network_proxy/ui/desktop/toolbar/setting/setting.dart'; + +class ProxySetting extends StatefulWidget { + final ProxyServer proxyServer; + + const ProxySetting({super.key, required this.proxyServer}); + + @override + State createState() { + return _ProxySettingState(); + } +} + +class _ProxySettingState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('代理设置', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500))), + body: ListView(children: [ + PortWidget(proxyServer: widget.proxyServer), + const Divider(height: 20, thickness: 0.3), + ListTile( + title: const Text('外部代理'), + trailing: const Icon(Icons.keyboard_arrow_right), + onTap: () { + showDialog( + context: context, builder: (_) => ExternalProxyDialog(configuration: widget.proxyServer.configuration)); + }, + ), + ]), + ); + } +} + +class ExternalProxyDialog extends StatefulWidget { + final Configuration configuration; + + const ExternalProxyDialog({super.key, required this.configuration}); + + @override + State createState() { + return _ExternalProxyDialogState(); + } +} + +class _ExternalProxyDialogState extends State { + final formKey = GlobalKey(); + late ProxyInfo externalProxy; + + @override + void initState() { + super.initState(); + externalProxy = ProxyInfo(); + if (widget.configuration.externalProxy != null) { + externalProxy = ProxyInfo.fromJson(widget.configuration.externalProxy!.toJson()); + } + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + scrollable: true, + title: const Text("外部代理设置", style: TextStyle(fontSize: 15)), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text("取消")), + TextButton( + onPressed: () async { + if (!formKey.currentState!.validate()) { + return; + } + submit(); + }, + child: const Text("确定")) + ], + content: Form( + key: formKey, + child: Column(mainAxisSize: MainAxisSize.min, children: [ + const SizedBox(height: 10), + Row(children: [ + const Text("是否启用:"), + Expanded( + child: Switch( + value: externalProxy.enabled, + onChanged: (val) { + setState(() => externalProxy.enabled = val); + }, + )) + ]), + Row(children: [ + const Text("地址:"), + Expanded( + child: TextFormField( + initialValue: externalProxy.host, + validator: (val) => val == null || val.isEmpty ? "地址不能为空" : null, + onChanged: (val) => externalProxy.host = val, + )) + ]), + Row(children: [ + const Text("端口:"), + Expanded( + child: TextFormField( + initialValue: externalProxy.port?.toString() ?? '', + inputFormatters: [ + LengthLimitingTextInputFormatter(5), + FilteringTextInputFormatter.allow(RegExp("[0-9]")) + ], + onChanged: (val) => externalProxy.port = int.parse(val), + validator: (val) => val == null || val.isEmpty ? "端口不能为空" : null, + decoration: const InputDecoration(), + )) + ]), + ]))); + } + + submit() async { + bool setting = true; + if (externalProxy.enabled) { + try { + var socket = await Socket.connect(externalProxy.host, externalProxy.port!, timeout: const Duration(seconds: 1)); + socket.destroy(); + } on SocketException catch (_) { + setting = false; + if (context.mounted) { + await showDialog( + context: context, + builder: (_) => AlertDialog( + title: const Text("外部代理连接失败"), + content: const Text('网络不通所有接口将会访问失败,是否继续设置外部代理。', style: TextStyle(fontSize: 12)), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text("取消")), + TextButton( + onPressed: () { + setting = true; + Navigator.of(context).pop(); + }, + child: const Text("确定")) + ], + )); + } + } + } + + if (setting) { + widget.configuration.externalProxy = externalProxy; + widget.configuration.flushConfig(); + } + + if (context.mounted) Navigator.of(context).pop(); + } +} diff --git a/pubspec.lock b/pubspec.lock index affe4a3..04cd7eb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: "direct main" description: name: basic_utils - sha256: "1fb8c5493fc1b9500512b2e153c0b9bcc9e281621cde7f810420f4761be9e38d" + sha256: "2064b21d3c41ed7654bc82cc476fd65542e04d60059b74d5eed490a4da08fc6c" url: "https://pub.flutter-io.cn" source: hosted - version: "5.6.1" + version: "5.7.0" boolean_selector: dependency: transitive description: @@ -174,18 +174,18 @@ packages: dependency: transitive description: name: file_selector_android - sha256: d41e165d6f798ca941d536e5dc93494d50e78c571c28ad60cfe0b0fefeb9f1e7 + sha256: b7556052dbcc25ef88f6eba45ab98aa5600382af8dfdabc9d644a93d97b7be7f url: "https://pub.flutter-io.cn" source: hosted - version: "0.5.0+3" + version: "0.5.0+4" file_selector_ios: dependency: transitive description: name: file_selector_ios - sha256: b3fbdda64aa2e335df6e111f6b0f1bb968402ed81d2dd1fa4274267999aa32c2 + sha256: "2f48db7e338b2255101c35c604b7ca5ab588dce032db7fc418a2fe5f28da63f8" url: "https://pub.flutter-io.cn" source: hosted - version: "0.5.1+6" + version: "0.5.1+7" file_selector_linux: dependency: transitive description: @@ -243,10 +243,10 @@ packages: dependency: "direct main" description: name: flutter_code_editor - sha256: "2e48e2a09c4205991787f299cd101f66f28e6845f882df543ae0f4260f9e2c67" + sha256: "56673b62f2d844c1a2b0cf43a4495be0d6123111de5f33887781d2d3b39a77b6" url: "https://pub.flutter-io.cn" source: hosted - version: "0.2.9" + version: "0.3.1" flutter_highlight: dependency: transitive description: @@ -275,10 +275,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: f185ac890306b5779ecbd611f52502d8d4d63d27703ef73161ca0407e815f02c + sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.16" + version: "2.0.17" flutter_test: dependency: "direct dev" description: flutter @@ -417,6 +417,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.4" + mocktail: + dependency: transitive + description: + name: mocktail + sha256: bac151b31e4ed78bd59ab89aa4c0928f297b1180186d5daf03734519e5f596c1 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.1" path: dependency: transitive description: @@ -437,10 +445,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" + sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 url: "https://pub.flutter-io.cn" source: hosted - version: "2.2.0" + version: "2.2.1" path_provider_foundation: dependency: transitive description: @@ -650,66 +658,66 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27" + sha256: b1c9e98774adf8820c96fbc7ae3601231d324a7d5ebd8babe27b6dfac91357ba url: "https://pub.flutter-io.cn" source: hosted - version: "6.1.14" + version: "6.2.1" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330 + sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" url: "https://pub.flutter-io.cn" source: hosted - version: "6.1.0" + version: "6.2.0" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "7c65021d5dee51813d652357bc65b8dd4a6177082a9966bc8ba6ee477baa795f" + sha256: "4ac97281cf60e2e8c5cc703b2b28528f9b50c8f7cebc71df6bdf0845f647268a" url: "https://pub.flutter-io.cn" source: hosted - version: "6.1.5" + version: "6.2.0" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: b651aad005e0cb06a01dbd84b428a301916dc75f0e7ea6165f80057fee2d8e8e + sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd" url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.6" + version: "3.1.0" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: b55486791f666e62e0e8ff825e58a023fd6b1f71c49926483f1128d3bbd8fe88 + sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.7" + version: "3.1.0" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618" + sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.5" + version: "2.2.0" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "2942294a500b4fa0b918685aff406773ba0a4cd34b7f42198742a94083020ce5" + sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.20" + version: "2.2.0" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "95fef3129dc7cfaba2bc3d5ba2e16063bb561fc6d78e63eee16162bc70029069" + sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc" url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.8" + version: "3.1.0" uuid: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index af19a33..7aee169 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: sdk: flutter crypto: ^3.0.3 cupertino_icons: ^1.0.2 - basic_utils: ^5.6.1 + basic_utils: ^5.7.0 logger: ^2.0.1 date_format: ^2.0.7 window_manager: ^0.3.7 @@ -21,7 +21,7 @@ dependencies: url: https://gitee.com/wanghongenpin/flutter-plugins.git path: packages/desktop_multi_window path_provider: ^2.1.1 - url_launcher: ^6.1.14 + url_launcher: ^6.2.1 proxy_manager: ^0.0.3 qr_flutter: ^4.1.0 easy_permission: ^1.0.0