mirror of
https://github.com/wanghongenpin/proxypin.git
synced 2026-05-20 16:15:47 +08:00
左右拖动布局
This commit is contained in:
@@ -2,6 +2,7 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:network_proxy/network/bin/server.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/left/domain.dart';
|
||||||
import 'package:network_proxy/ui/panel.dart';
|
import 'package:network_proxy/ui/panel.dart';
|
||||||
import 'package:network_proxy/ui/toolbar/toolbar.dart';
|
import 'package:network_proxy/ui/toolbar/toolbar.dart';
|
||||||
@@ -84,19 +85,12 @@ class _NetworkHomePagePageState extends State<NetworkHomePage> implements EventL
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
final domainWidget = DomainWidget(key: domainStateKey, panel: panel);
|
final domainWidget = DomainWidget(key: domainStateKey, panel: panel);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: Tab(
|
appBar: Tab(
|
||||||
child: Toolbar(proxyServer, domainStateKey),
|
child: Toolbar(proxyServer, domainStateKey),
|
||||||
),
|
),
|
||||||
body: Padding(
|
body: VerticalSplitView(ratio: 0.3, minRatio: 0.15, maxRatio: 0.9, left: domainWidget, right: panel));
|
||||||
padding: const EdgeInsets.only(top: 5),
|
|
||||||
child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
|
||||||
SizedBox(width: 400, child: domainWidget),
|
|
||||||
const VerticalDivider(),
|
|
||||||
Expanded(flex: 100, child: panel),
|
|
||||||
])));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
83
lib/ui/component/split_view.dart
Normal file
83
lib/ui/component/split_view.dart
Normal file
@@ -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<VerticalSplitView> createState() => _VerticalSplitViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _VerticalSplitViewState extends State<VerticalSplitView> {
|
||||||
|
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: <Widget>[
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,8 +6,12 @@ class ColorTransition extends StatefulWidget {
|
|||||||
final Duration duration;
|
final Duration duration;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
const ColorTransition({super.key, required this.begin, this.end = Colors.transparent,
|
const ColorTransition(
|
||||||
this.duration = const Duration(milliseconds: 500), required this.child});
|
{super.key,
|
||||||
|
required this.begin,
|
||||||
|
this.end = Colors.transparent,
|
||||||
|
this.duration = const Duration(milliseconds: 500),
|
||||||
|
required this.child});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ColorTransition> createState() {
|
State<ColorTransition> createState() {
|
||||||
@@ -22,7 +26,6 @@ class ColorTransitionState extends State<ColorTransition> with SingleTickerProvi
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
print("AnimationController");
|
|
||||||
|
|
||||||
//创建动画控制器
|
//创建动画控制器
|
||||||
_animationController = AnimationController(
|
_animationController = AnimationController(
|
||||||
@@ -39,7 +42,7 @@ class ColorTransitionState extends State<ColorTransition> with SingleTickerProvi
|
|||||||
_animation = ColorTween(begin: widget.begin, end: widget.end).animate(_animationController);
|
_animation = ColorTween(begin: widget.begin, end: widget.end).animate(_animationController);
|
||||||
|
|
||||||
//添加到事件队列
|
//添加到事件队列
|
||||||
Future.delayed(const Duration(milliseconds: 80), () {
|
Future.delayed(const Duration(milliseconds: 150), () {
|
||||||
_animationController.forward();
|
_animationController.forward();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@ import 'dart:collection';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:network_proxy/network/http/http.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 'package:network_proxy/ui/left/path.dart';
|
||||||
|
|
||||||
import '../../network/channel.dart';
|
import '../../network/channel.dart';
|
||||||
@@ -109,7 +109,8 @@ class _HeaderBodyState extends State<HeaderBody> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(children: [
|
return Column(
|
||||||
|
children: [
|
||||||
_hostWidget(widget.header),
|
_hostWidget(widget.header),
|
||||||
Offstage(offstage: !selected, child: Column(children: widget._body.toList()))
|
Offstage(offstage: !selected, child: Column(children: widget._body.toList()))
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ class _DomainFilterState extends State<DomainFilter> {
|
|||||||
String? host;
|
String? host;
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
scrollable: true,
|
scrollable: true,
|
||||||
@@ -168,7 +169,7 @@ class _DomainFilterState extends State<DomainFilter> {
|
|||||||
onSaved: (val) => host = val)
|
onSaved: (val) => host = val)
|
||||||
]))),
|
]))),
|
||||||
actions: [
|
actions: [
|
||||||
ElevatedButton(
|
FilledButton(
|
||||||
child: const Text("添加"),
|
child: const Text("添加"),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
(formKey.currentState as FormState).save();
|
(formKey.currentState as FormState).save();
|
||||||
@@ -182,6 +183,11 @@ class _DomainFilterState extends State<DomainFilter> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Navigator.of(context).pop();
|
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<DomainList> {
|
class _DomainListState extends State<DomainList> {
|
||||||
late Map<int, bool> selected = {};
|
late Map<int, bool> selected = {};
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.only(top: 10),
|
padding: const EdgeInsets.only(top: 10),
|
||||||
height: 300,
|
height: 300,
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ class _RequestRewriteState extends State<RequestRewrite> {
|
|||||||
void add([int currentIndex = -1]) {
|
void add([int currentIndex = -1]) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return RuleAddDialog(
|
return RuleAddDialog(
|
||||||
requestRewrites: widget.proxyServer.requestRewrites,
|
requestRewrites: widget.proxyServer.requestRewrites,
|
||||||
@@ -187,6 +188,11 @@ class RuleAddDialog extends StatelessWidget {
|
|||||||
onChange.call();
|
onChange.call();
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}
|
}
|
||||||
|
}),
|
||||||
|
ElevatedButton(
|
||||||
|
child: const Text("关闭"),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
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/setting/setting.dart';
|
||||||
import 'package:network_proxy/ui/toolbar/ssl/ssl.dart';
|
import 'package:network_proxy/ui/toolbar/ssl/ssl.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
|
||||||
|
|
||||||
import '../../network/bin/server.dart';
|
import '../../network/bin/server.dart';
|
||||||
import '../left/domain.dart';
|
import '../left/domain.dart';
|
||||||
@@ -21,7 +21,27 @@ class Toolbar extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ToolbarState extends State<Toolbar> with WindowListener {
|
class _ToolbarState extends State<Toolbar> {
|
||||||
|
@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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
|
|||||||
Reference in New Issue
Block a user