diff --git a/lib/main.dart b/lib/main.dart index de5ee98..f18d198 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:network_proxy/network/bin/server.dart'; +import 'package:network_proxy/ui/component/split_view.dart'; import 'package:network_proxy/ui/left/domain.dart'; import 'package:network_proxy/ui/panel.dart'; import 'package:network_proxy/ui/toolbar/toolbar.dart'; @@ -84,19 +85,12 @@ class _NetworkHomePagePageState extends State implements EventL @override Widget build(BuildContext context) { - final domainWidget = DomainWidget(key: domainStateKey, panel: panel); return Scaffold( appBar: Tab( child: Toolbar(proxyServer, domainStateKey), ), - 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), - ]))); + body: VerticalSplitView(ratio: 0.3, minRatio: 0.15, maxRatio: 0.9, left: domainWidget, right: panel)); } } diff --git a/lib/ui/component/split_view.dart b/lib/ui/component/split_view.dart new file mode 100644 index 0000000..f7bf937 --- /dev/null +++ b/lib/ui/component/split_view.dart @@ -0,0 +1,83 @@ +import 'package:flutter/material.dart'; + +class VerticalSplitView extends StatefulWidget { + final Widget left; + final Widget right; + final double ratio; + final double minRatio; + final double maxRatio; + + const VerticalSplitView( + {super.key, required this.left, required this.right, this.ratio = 0.5, this.minRatio = 0, this.maxRatio = 1}) + : assert(ratio >= 0 && ratio <= 1); + + @override + State createState() => _VerticalSplitViewState(); +} + +class _VerticalSplitViewState extends State { + final _dividerWidth = 10.0; + + //from 0-1 + late double _ratio; + double _maxWidth = double.infinity; + + get _width1 => _ratio * _maxWidth; + + get _width2 => (1 - _ratio) * _maxWidth; + + @override + void initState() { + super.initState(); + _ratio = widget.ratio; + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (context, BoxConstraints constraints) { + if (_maxWidth != constraints.maxWidth) { + _maxWidth = constraints.maxWidth - _dividerWidth; + } + + return SizedBox( + width: constraints.maxWidth, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: _width1 - 5, + child: widget.left, + ), + GestureDetector( + behavior: HitTestBehavior.translucent, + child: MouseRegion( + cursor: SystemMouseCursors.resizeColumn, + child: SizedBox( + width: _dividerWidth, + height: double.infinity, + child: (_ratio <= 0 || _ratio >= 1) + ? const Icon(Icons.drag_handle, size: 16) + : const VerticalDivider(thickness: 2), + )), + onPanUpdate: (DragUpdateDetails details) { + setState(() { + _ratio += details.delta.dx / _maxWidth; + + if (_ratio > widget.maxRatio) { + _ratio = widget.maxRatio; + } else if (_ratio < widget.minRatio) { + _ratio = widget.minRatio; + } + }); + }, + ), + SizedBox( + width: _width2, + child: widget.right, + ), + ], + ), + ); + }); + } +} diff --git a/lib/ui/components.dart b/lib/ui/component/transition.dart similarity index 83% rename from lib/ui/components.dart rename to lib/ui/component/transition.dart index 6236aaf..387423f 100644 --- a/lib/ui/components.dart +++ b/lib/ui/component/transition.dart @@ -6,8 +6,12 @@ class ColorTransition extends StatefulWidget { 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}); + const ColorTransition( + {super.key, + required this.begin, + this.end = Colors.transparent, + this.duration = const Duration(milliseconds: 500), + required this.child}); @override State createState() { @@ -22,7 +26,6 @@ class ColorTransitionState extends State with SingleTickerProvi @override void initState() { super.initState(); - print("AnimationController"); //创建动画控制器 _animationController = AnimationController( @@ -39,7 +42,7 @@ class ColorTransitionState extends State with SingleTickerProvi _animation = ColorTween(begin: widget.begin, end: widget.end).animate(_animationController); //添加到事件队列 - Future.delayed(const Duration(milliseconds: 80), () { + Future.delayed(const Duration(milliseconds: 150), () { _animationController.forward(); }); } diff --git a/lib/ui/left/domain.dart b/lib/ui/left/domain.dart index 3acad55..2281b8e 100644 --- a/lib/ui/left/domain.dart +++ b/lib/ui/left/domain.dart @@ -2,7 +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/component/transition.dart'; import 'package:network_proxy/ui/left/path.dart'; import '../../network/channel.dart'; @@ -109,7 +109,8 @@ class _HeaderBodyState extends State { @override Widget build(BuildContext context) { - return Column(children: [ + return Column( + children: [ _hostWidget(widget.header), Offstage(offstage: !selected, child: Column(children: widget._body.toList())) ]); diff --git a/lib/ui/toolbar/setting/filter.dart b/lib/ui/toolbar/setting/filter.dart index abb6456..3f24d53 100644 --- a/lib/ui/toolbar/setting/filter.dart +++ b/lib/ui/toolbar/setting/filter.dart @@ -155,6 +155,7 @@ class _DomainFilterState extends State { String? host; showDialog( context: context, + barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( scrollable: true, @@ -168,7 +169,7 @@ class _DomainFilterState extends State { onSaved: (val) => host = val) ]))), actions: [ - ElevatedButton( + FilledButton( child: const Text("添加"), onPressed: () { (formKey.currentState as FormState).save(); @@ -182,6 +183,11 @@ class _DomainFilterState extends State { } } Navigator.of(context).pop(); + }), + ElevatedButton( + child: const Text("关闭"), + onPressed: () { + Navigator.of(context).pop(); }) ]); }); @@ -211,9 +217,9 @@ class DomainList extends StatefulWidget { class _DomainListState extends State { late Map selected = {}; - @override Widget build(BuildContext context) { + return Container( padding: const EdgeInsets.only(top: 10), height: 300, diff --git a/lib/ui/toolbar/setting/request_rewrite.dart b/lib/ui/toolbar/setting/request_rewrite.dart index b876e67..62d30e7 100644 --- a/lib/ui/toolbar/setting/request_rewrite.dart +++ b/lib/ui/toolbar/setting/request_rewrite.dart @@ -94,6 +94,7 @@ class _RequestRewriteState extends State { void add([int currentIndex = -1]) { showDialog( context: context, + barrierDismissible: false, builder: (BuildContext context) { return RuleAddDialog( requestRewrites: widget.proxyServer.requestRewrites, @@ -187,6 +188,11 @@ class RuleAddDialog extends StatelessWidget { onChange.call(); Navigator.of(context).pop(); } + }), + ElevatedButton( + child: const Text("关闭"), + onPressed: () { + Navigator.of(context).pop(); }) ]); } diff --git a/lib/ui/toolbar/toolbar.dart b/lib/ui/toolbar/toolbar.dart index 099d95e..bf67062 100644 --- a/lib/ui/toolbar/toolbar.dart +++ b/lib/ui/toolbar/toolbar.dart @@ -1,9 +1,9 @@ import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:network_proxy/ui/toolbar/setting/setting.dart'; import 'package:network_proxy/ui/toolbar/ssl/ssl.dart'; -import 'package:window_manager/window_manager.dart'; import '../../network/bin/server.dart'; import '../left/domain.dart'; @@ -21,7 +21,27 @@ class Toolbar extends StatefulWidget { } } -class _ToolbarState extends State with WindowListener { +class _ToolbarState extends State { + @override + void initState() { + super.initState(); + RawKeyboard.instance.addListener(onKeyEvent); + } + + void onKeyEvent(RawKeyEvent event) { + if (event.isKeyPressed(LogicalKeyboardKey.escape)) { + if (ModalRoute.of(context)?.isCurrent == false) { + Navigator.of(context).pop(); + } + } + } + + @override + void dispose() { + RawKeyboard.instance.removeListener(onKeyEvent); + super.dispose(); + } + @override Widget build(BuildContext context) { return Row(