新增请求动画效果

This commit is contained in:
wanghongen
2023-06-27 01:34:53 +08:00
parent 221e11f17f
commit 1ad938c533
13 changed files with 210 additions and 116 deletions

View File

@@ -37,13 +37,15 @@ class FluentApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
ThemeData(brightness: Brightness.dark, useMaterial3: false);
return ValueListenableBuilder<ThemeMode>(
valueListenable: themeNotifier,
builder: (_, ThemeMode currentMode, __) {
return MaterialApp(
title: 'ProxyPin',
debugShowCheckedModeBanner: false,
theme: ThemeData.light(useMaterial3: true),
darkTheme: ThemeData(brightness: Brightness.dark, useMaterial3: false),
darkTheme: ThemeData.dark(useMaterial3: false),
themeMode: currentMode,
home: const NetworkHomePage(),
);
@@ -59,37 +61,42 @@ class NetworkHomePage extends StatefulWidget {
}
class _NetworkHomePagePageState extends State<NetworkHomePage> implements EventListener {
final domainStateKey = GlobalKey<DomainWidgetState>();
final NetworkTabController panel = NetworkTabController();
late DomainWidget domainWidget;
late ProxyServer proxyServer;
@override
void onRequest(Channel channel, HttpRequest request) {
domainWidget.add(channel, request);
domainStateKey.currentState!.add(channel, request);
}
@override
void onResponse(Channel channel, HttpResponse response) {
domainWidget.addResponse(channel, response);
domainStateKey.currentState!.addResponse(channel, response);
}
@override
void initState() {
super.initState();
domainWidget = DomainWidget(panel: panel);
proxyServer = ProxyServer(listener: this);
}
@override
Widget build(BuildContext context) {
final domainWidget = DomainWidget(key: domainStateKey, panel: panel);
return Scaffold(
appBar: Tab(
child: Toolbar(proxyServer, domainWidget),
child: Toolbar(proxyServer, domainStateKey),
),
body: Row(children: [
SizedBox(width: 400, child: domainWidget),
const VerticalDivider(),
Expanded(flex: 100, child: domainWidget.panel),
]));
body: Padding(
padding: const EdgeInsets.only(top: 5),
child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
SizedBox(width: 400, child: domainWidget),
const VerticalDivider(),
Expanded(flex: 100, child: panel),
])));
}
}

View File

@@ -1,5 +1,7 @@
import 'dart:io';
import 'package:network_proxy/utils/ip.dart';
class SystemProxy {
/// 设置系统代理
static void setSystemProxy(int port, bool enableSsl) async {
@@ -18,12 +20,13 @@ class SystemProxy {
}
var host = match.namedGroup('host');
var port = match.namedGroup('port');
var name = await hardwarePort();
var results = await Process.run('bash', [
'-c',
_concatCommands([
'networksetup -setwebproxy wi-fi $host $port',
enableSsl == true ? 'networksetup -setsecurewebproxy wi-fi $host $port' : '',
'networksetup -setproxybypassdomains wi-fi 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12, 127.0.0.1, localhost, *.local, timestamp.apple.com, sequoia.apple.com, seed-sequoia.siri.apple.com, *.google.com, *.googleapis.com',
'networksetup -setwebproxy $name $host $port',
enableSsl == true ? 'networksetup -setsecurewebproxy $name $host $port' : '',
'networksetup -setproxybypassdomains $name 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12 127.0.0.1 localhost *.local timestamp.apple.com sequoia.apple.com seed-sequoia.siri.apple.com *.google.com',
])
]);
print('set proxyServer, exitCode: ${results.exitCode}, stdout: ${results.stdout}');
@@ -32,25 +35,65 @@ class SystemProxy {
static Future<bool> setProxyEnableMacOS(bool proxyEnable, bool enableSsl) async {
var proxyMode = proxyEnable ? 'on' : 'off';
var name = await hardwarePort();
var results = await Process.run('bash', [
'-c',
_concatCommands([
'networksetup -setwebproxystate wi-fi $proxyMode',
enableSsl ? 'networksetup -setsecurewebproxystate wi-fi $proxyMode' : '',
'networksetup -setwebproxystate $name $proxyMode',
enableSsl ? 'networksetup -setsecurewebproxystate $name $proxyMode' : '',
])
]);
return results.exitCode == 0;
}
static Future<bool> setSslProxyEnableMacOS(bool proxyEnable, port) async {
var name = await hardwarePort();
var results = await Process.run('bash', [
'-c',
proxyEnable
? 'networksetup -setsecurewebproxy $name 127.0.0.1 $port'
: 'networksetup -setsecurewebproxystate $name off',
]);
return results.exitCode == 0;
}
static Future<String> hardwarePort() async {
var name = await networkName();
var results = await Process.run('bash', [
'-c',
_concatCommands([
proxyEnable
? 'networksetup -setsecurewebproxy wi-fi 127.0.0.1 $port'
: 'networksetup -setsecurewebproxystate wi-fi off',
'networksetup -listnetworkserviceorder |grep "Device: $name" -A 1 |grep "Hardware Port" |awk -F ": " \'{print \$2}\'',
])
]);
return results.stdout.toString().split(", ")[0];
}
static Future<bool> _setProxyServerWindows(String proxyServer) async {
var results = await Process.run('reg', [
'add',
'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'/v',
'ProxyServer',
'/f',
'/d',
proxyServer,
]);
Process.run('reg', [
'add',
'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'/v',
'ProxyOverride',
'/t',
'REG_SZ',
'/d',
'192.168.0.*;10.0.0.*;172.16.0.*;127.0.0.1;localhost;*.local',
'/f',
]);
print('set proxyServer $proxyServer, exitCode: ${results.exitCode}, stdout: ${results.stderr}');
return results.exitCode == 0;
}
@@ -69,21 +112,12 @@ class SystemProxy {
return results.exitCode == 0;
}
static Future<bool> _setProxyServerWindows(String proxyServer) async {
var results = await Process.run('reg', [
'add',
'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'/v',
'ProxyServer',
'/f',
'/d',
proxyServer,
]);
print('set proxyServer $proxyServer, exitCode: ${results.exitCode}, stdout: ${results.stderr}');
return results.exitCode == 0;
}
static _concatCommands(List<String> commands) {
return commands.where((element) => element.isNotEmpty).join(' && ');
}
}
void main() async {
var r = await SystemProxy.hardwarePort();
print(r);
}

View File

@@ -1 +1,65 @@
import 'package:flutter/material.dart';
class ColorTransition extends StatefulWidget {
final Color begin;
final Color end;
final Duration duration;
final Widget child;
const ColorTransition({super.key, required this.begin, this.end = Colors.transparent,
this.duration = const Duration(milliseconds: 500), required this.child});
@override
State<ColorTransition> createState() {
return ColorTransitionState();
}
}
class ColorTransitionState extends State<ColorTransition> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation _animation;
@override
void initState() {
super.initState();
print("AnimationController");
//创建动画控制器
_animationController = AnimationController(
vsync: this,
duration: widget.duration,
);
//添加动画执行刷新监听
_animationController.addListener(() {
setState(() {});
});
//颜色动画变化
_animation = ColorTween(begin: widget.begin, end: widget.end).animate(_animationController);
//添加到事件队列
Future.delayed(const Duration(milliseconds: 80), () {
_animationController.forward();
});
}
show() {
_animationController.reset();
_animationController.forward();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
color: _animation.value,
child: widget.child,
);
}
}

View File

@@ -2,6 +2,7 @@ import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:network_proxy/network/http/http.dart';
import 'package:network_proxy/ui/components.dart';
import 'package:network_proxy/ui/left/path.dart';
import '../../network/channel.dart';
@@ -12,42 +13,25 @@ import '../panel.dart';
class DomainWidget extends StatefulWidget {
final NetworkTabController panel;
DomainWidget({required this.panel}) : super(key: GlobalKey<_DomainWidgetState>());
void add(Channel channel, HttpRequest request) {
var state = key as GlobalKey<_DomainWidgetState>;
state.currentState?.add(channel, request);
}
void addResponse(Channel channel, HttpResponse response) {
var state = key as GlobalKey<_DomainWidgetState>;
state.currentState?.addResponse(channel, response);
}
void clean() {
var state = key as GlobalKey<_DomainWidgetState>;
panel.change(null, null);
state.currentState?.clean();
}
const DomainWidget({super.key, required this.panel});
@override
State<StatefulWidget> createState() {
return _DomainWidgetState();
return DomainWidgetState();
}
}
class _DomainWidgetState extends State<DomainWidget> {
class DomainWidgetState extends State<DomainWidget> {
LinkedHashMap<HostAndPort, HeaderBody> containerMap = LinkedHashMap<HostAndPort, HeaderBody>();
@override
Widget build(BuildContext context) {
var list = containerMap.values;
return ListView.builder(
itemBuilder: (BuildContext context, int index) => list.elementAt(index), itemCount: list.length);
return SingleChildScrollView(child: Column(children: list.toList()));
}
///添加请求
void add(Channel channel, HttpRequest request) {
add(Channel channel, HttpRequest request) {
HostAndPort hostAndPort = channel.getAttribute(AttributeKeys.host);
HeaderBody? headerBody = containerMap[hostAndPort];
var listURI = PathRow(request, widget.panel);
@@ -64,14 +48,15 @@ class _DomainWidgetState extends State<DomainWidget> {
}
///添加响应
void addResponse(Channel channel, HttpResponse response) {
addResponse(Channel channel, HttpResponse response) {
HostAndPort hostAndPort = channel.getAttribute(AttributeKeys.host);
HeaderBody? headerBody = containerMap[hostAndPort];
headerBody?.getBody(channel.id)?.add(response);
}
///清理
void clean() {
clean() {
widget.panel.change(null, null);
setState(() {
containerMap.clear();
});
@@ -107,6 +92,8 @@ class HeaderBody extends StatefulWidget {
}
class _HeaderBodyState extends State<HeaderBody> {
final GlobalKey<ColorTransitionState> transitionState = GlobalKey<ColorTransitionState>();
late bool selected;
@override
@@ -117,6 +104,7 @@ class _HeaderBodyState extends State<HeaderBody> {
changeState() {
setState(() {});
transitionState.currentState?.show();
}
@override
@@ -128,16 +116,21 @@ class _HeaderBodyState extends State<HeaderBody> {
}
Widget _hostWidget(String title) {
return ListTile(
leading: Icon(selected ? Icons.arrow_drop_down : Icons.arrow_right, size: 16),
dense: true,
horizontalTitleGap: 0,
visualDensity: const VisualDensity(vertical: -3.6),
title: Text(title, textAlign: TextAlign.left),
onTap: () {
setState(() {
selected = !selected;
});
});
return ColorTransition(
key: transitionState,
duration: const Duration(milliseconds: 1500),
begin: Colors.white30,
child: ListTile(
minLeadingWidth: 25,
leading: Icon(selected ? Icons.arrow_drop_down : Icons.arrow_right, size: 16),
dense: true,
horizontalTitleGap: 0,
visualDensity: const VisualDensity(vertical: -3.6),
title: Text(title, textAlign: TextAlign.left),
onTap: () {
setState(() {
selected = !selected;
});
}));
}
}

View File

@@ -36,6 +36,7 @@ class _PathRowState extends State<PathRow> {
var title = '${request.method.name} ${Uri.parse(request.uri).path}';
var time = formatDate(request.requestTime, [HH, ':', nn, ':', ss]);
return ListTile(
minLeadingWidth: 25,
leading: Icon(getIcon(), size: 16, color: widget.color),
title: Text(title, overflow: TextOverflow.ellipsis, maxLines: 1),
subtitle: Text(

View File

@@ -16,22 +16,22 @@ class NetworkTabController extends StatefulWidget {
final ValueWrap<HttpRequest> request = ValueWrap();
final ValueWrap<HttpResponse> response = ValueWrap();
NetworkTabController() : super(key: GlobalKey<_NetworkTabState>());
NetworkTabController() : super(key: GlobalKey<NetworkTabState>());
void change(HttpRequest? request, HttpResponse? response) {
this.request.set(request);
this.response.set(response);
var state = key as GlobalKey<_NetworkTabState>;
var state = key as GlobalKey<NetworkTabState>;
state.currentState?.changeState();
}
@override
State<StatefulWidget> createState() {
return _NetworkTabState();
return NetworkTabState();
}
}
class _NetworkTabState extends State<NetworkTabController> {
class NetworkTabState extends State<NetworkTabController> {
void changeState() {
setState(() {});
}

View File

@@ -144,10 +144,10 @@ class _DomainFilterState extends State<DomainFilter> {
@override
void dispose() {
super.dispose();
if (changed) {
widget.proxyServer.flushConfig();
}
super.dispose();
}
void add() {

View File

@@ -26,23 +26,32 @@ class _SettingState extends State<Setting> {
offset: const Offset(10, 30),
itemBuilder: (context) {
return [
PopupMenuItem<String>(child: PortWidget(proxyServer: widget.proxyServer)),
PopupMenuItem<String>(padding: const EdgeInsets.all(0), child: PortWidget(proxyServer: widget.proxyServer)),
const PopupMenuItem(
padding: EdgeInsets.all(0),
child: ThemeSetting(),
),
PopupMenuItem<String>(
padding: const EdgeInsets.all(0),
child: ListTile(
title: const Text("域名过滤"),
dense: true,
hoverColor: Colors.transparent,
focusColor: Colors.transparent,
trailing: const Icon(Icons.arrow_right),
onTap: () => _filter())),
PopupMenuItem<String>(
padding: const EdgeInsets.all(0),
child: ListTile(
title: const Text("请求重写"),
dense: true,
trailing: const Icon(Icons.arrow_right),
onTap: () => _reqeustRewrite())),
title: const Text("请求重写"),
dense: true,
hoverColor: Colors.transparent,
focusColor: Colors.transparent,
trailing: const Icon(Icons.arrow_right),
onTap: () => _reqeustRewrite(),
)),
PopupMenuItem<String>(
padding: const EdgeInsets.all(0),
child: const ListTile(title: Text("Github"), dense: true, trailing: Icon(Icons.arrow_right)),
onTap: () {
launchUrl(Uri.parse("https://github.com/wanghongenpin/network-proxy-flutter"));

View File

@@ -23,20 +23,26 @@ class _SslState extends State<SslWidget> {
offset: const Offset(10, 30),
itemBuilder: (context) {
return [
PopupMenuItem(child: _Switch(proxyServer: widget.proxyServer)),
PopupMenuItem(padding: const EdgeInsets.all(0), child: _Switch(proxyServer: widget.proxyServer)),
PopupMenuItem(
padding: const EdgeInsets.all(0),
child: ListTile(
dense: true,
title: const Text("安装根证书到系统"),
trailing: const Icon(Icons.arrow_right),
onTap: () {
pcCer();
},
)),
dense: true,
hoverColor: Colors.transparent,
focusColor: Colors.transparent,
title: const Text("安装根证书到系统"),
trailing: const Icon(Icons.arrow_right),
onTap: () {
pcCer();
},
)),
PopupMenuItem<String>(
padding: const EdgeInsets.all(0),
child: ListTile(
title: const Text("安装根证书到手机"),
dense: true,
hoverColor: Colors.transparent,
focusColor: Colors.transparent,
trailing: const Icon(Icons.arrow_right),
onTap: () async {
mobileCer(await localIp());
@@ -124,6 +130,7 @@ class _SwitchState extends State<_Switch> {
@override
Widget build(BuildContext context) {
return SwitchListTile(
hoverColor: Colors.transparent,
title: const Text("启用Https代理", style: TextStyle(fontSize: 12)),
visualDensity: const VisualDensity(horizontal: -4),
dense: true,

View File

@@ -11,9 +11,9 @@ import 'launch/launch.dart';
class Toolbar extends StatefulWidget {
final ProxyServer proxyServer;
final DomainWidget domainWidget;
final GlobalKey<DomainWidgetState> domainStateKey;
const Toolbar(this.proxyServer, this.domainWidget, {super.key});
const Toolbar(this.proxyServer, this.domainStateKey, {super.key});
@override
State<StatefulWidget> createState() {
@@ -33,7 +33,7 @@ class _ToolbarState extends State<Toolbar> with WindowListener {
tooltip: "清理",
icon: const Icon(Icons.cleaning_services_outlined),
onPressed: () {
widget.domainWidget.clean();
widget.domainStateKey.currentState?.clean();
}),
const Padding(padding: EdgeInsets.only(left: 30)),
SslWidget(proxyServer: widget.proxyServer),

View File

@@ -15,3 +15,7 @@ Future<String> localIp() async {
String ip = await NetworkInterface.list().then((interfaces) => interfaces.first.addresses.first.address);
return ip;
}
Future<String> networkName() {
return NetworkInterface.list().then((interfaces) => interfaces.first.name);
}