Optimize proxies page

Support mouse drag scroll

Adjust desktop ui
This commit is contained in:
chen08209
2024-07-15 22:06:09 +08:00
parent 2c3f4ae8a8
commit 82be4cc45f
45 changed files with 2312 additions and 1212 deletions

View File

@@ -0,0 +1,167 @@
import 'dart:io';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/plugins/app.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'chip.dart';
import 'list.dart';
class ConnectionItem extends StatelessWidget {
final Connection connection;
final Function(String)? onClick;
final Widget? trailing;
const ConnectionItem({
super.key,
required this.connection,
this.onClick,
this.trailing,
});
Future<ImageProvider?> _getPackageIcon(Connection connection) async {
return await app?.getPackageIcon(connection.metadata.process);
}
String _getRequestText(Metadata metadata) {
var text = "${metadata.network}://";
final ips = [
metadata.host,
metadata.destinationIP,
].where((ip) => ip.isNotEmpty);
text += ips.join("/");
text += ":${metadata.destinationPort}";
return text;
}
String _getSourceText(Connection connection) {
final metadata = connection.metadata;
if (metadata.process.isEmpty) {
return connection.start.lastUpdateTimeDesc;
}
return "${metadata.process} · ${connection.start.lastUpdateTimeDesc}";
}
@override
Widget build(BuildContext context) {
if (!Platform.isAndroid) {
return ListItem(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 4,
),
tileTitleAlignment: ListTileTitleAlignment.titleHeight,
title: Text(
_getRequestText(connection.metadata),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 8,
),
Text(
_getSourceText(connection),
),
const SizedBox(
height: 8,
),
Wrap(
runSpacing: 6,
spacing: 6,
children: [
for (final chain in connection.chains)
CommonChip(
label: chain,
onPressed: () {
if (onClick == null) return;
onClick!(chain);
},
),
],
),
],
),
trailing: trailing,
);
}
return Selector<ClashConfig, bool>(
selector: (_, clashConfig) =>
clashConfig.findProcessMode == FindProcessMode.always,
builder: (_, value, child) {
return ListItem(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 4,
),
tileTitleAlignment: ListTileTitleAlignment.titleHeight,
leading: value
? GestureDetector(
onTap: () {
if (onClick == null) return;
final process = connection.metadata.process;
if(process.isEmpty) return;
onClick!(process);
},
child: Container(
margin: const EdgeInsets.only(top: 4),
width: 48,
height: 48,
child: FutureBuilder<ImageProvider?>(
future: _getPackageIcon(connection),
builder: (_, snapshot) {
if (!snapshot.hasData && snapshot.data == null) {
return Container();
} else {
return Image(
image: snapshot.data!,
gaplessPlayback: true,
width: 48,
height: 48,
);
}
},
),
),
)
: null,
title: Text(
_getRequestText(connection.metadata),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 8,
),
Text(
_getSourceText(connection),
),
const SizedBox(
height: 8,
),
Wrap(
runSpacing: 6,
spacing: 6,
children: [
for (final chain in connection.chains)
CommonChip(
label: chain,
onPressed: () {
if (onClick == null) return;
onClick!(chain);
},
),
],
),
],
),
trailing: trailing,
);
},
);
}
}

View File

@@ -5,7 +5,7 @@ import 'package:fl_clash/widgets/open_container.dart';
import 'package:flutter/material.dart';
import 'card.dart';
import 'extend_page.dart';
import 'sheet.dart';
import 'scaffold.dart';
class Delegate {

View File

@@ -119,14 +119,21 @@ class CommonScaffoldState extends State<CommonScaffold> {
child: Stack(
alignment: Alignment.bottomCenter,
children: [
ValueListenableBuilder(
ValueListenableBuilder<List<Widget>>(
valueListenable: _actions,
builder: (_, actions, __) {
final realActions =
actions.isNotEmpty ? actions : widget.actions;
return AppBar(
automaticallyImplyLeading: widget.automaticallyImplyLeading,
leading: widget.leading,
title: Text(widget.title),
actions: actions.isNotEmpty ? actions : widget.actions,
actions: [
...?realActions,
const SizedBox(
width: 8,
)
],
);
},
),

View File

@@ -54,3 +54,34 @@ showExtendPage(
),
);
}
showSheet({
required BuildContext context,
required WidgetBuilder builder,
required String title,
bool isScrollControlled = true,
double width = 320,
}) {
final viewMode = globalState.appController.appState.viewMode;
final isMobile = viewMode == ViewMode.mobile;
if (isMobile) {
showModalBottomSheet(
context: context,
isScrollControlled: isScrollControlled,
builder: builder,
showDragHandle: true,
useSafeArea: true,
);
} else {
showModalSideSheet(
useSafeArea: true,
isScrollControlled: isScrollControlled,
context: context,
constraints: BoxConstraints(
maxWidth: width,
),
body: builder(context),
title: title,
);
}
}

View File

@@ -84,8 +84,11 @@ class _SideSheetState extends State<SideSheet> {
borderRadius: BorderRadius.circular(0),
);
final BoxConstraints constraints =
widget.constraints ?? const BoxConstraints(maxWidth: 320);
final BoxConstraints constraints = widget.constraints ??
const BoxConstraints(
maxWidth: 320,
minWidth: 320
);
final Clip clipBehavior = widget.clipBehavior ?? Clip.none;
@@ -403,27 +406,26 @@ class _ModalSideSheetState<T> extends State<_ModalSideSheet<T>> {
}
class ModalSideSheetRoute<T> extends PopupRoute<T> {
ModalSideSheetRoute({
required this.builder,
this.capturedThemes,
this.barrierLabel,
this.barrierOnTapHint,
this.backgroundColor,
this.elevation,
this.shape,
this.clipBehavior,
this.constraints,
this.modalBarrierColor,
this.isDismissible = true,
this.isScrollControlled = false,
this.scrollControlDisabledMaxHeightRatio =
_defaultScrollControlDisabledMaxHeightRatio,
super.settings,
this.transitionAnimationController,
this.anchorPoint,
this.useSafeArea = false,
super.filter
});
ModalSideSheetRoute(
{required this.builder,
this.capturedThemes,
this.barrierLabel,
this.barrierOnTapHint,
this.backgroundColor,
this.elevation,
this.shape,
this.clipBehavior,
this.constraints,
this.modalBarrierColor,
this.isDismissible = true,
this.isScrollControlled = false,
this.scrollControlDisabledMaxHeightRatio =
_defaultScrollControlDisabledMaxHeightRatio,
super.settings,
this.transitionAnimationController,
this.anchorPoint,
this.useSafeArea = false,
super.filter});
final WidgetBuilder builder;
@@ -601,7 +603,9 @@ Future<T?> showModalSideSheet<T>({
width: kToolbarHeight,
child: BackButton(),
),
const SizedBox(width: 8,),
const SizedBox(
width: 8,
),
Expanded(
child: Text(
title,
@@ -617,6 +621,7 @@ Future<T?> showModalSideSheet<T>({
),
),
Expanded(
flex: 1,
child: body,
),
],

View File

@@ -35,6 +35,9 @@ class _TrayContainerState extends State<TrayContainer> with TrayListener {
await trayManager.setIcon(
other.getTrayIconPath(),
);
await trayManager.setToolTip(
appName,
);
isTrayInit = true;
}
}
@@ -44,6 +47,9 @@ class _TrayContainerState extends State<TrayContainer> with TrayListener {
await trayManager.setIcon(
other.getTrayIconPath(),
);
await trayManager.setToolTip(
appName,
);
}
updateMenu(TrayContainerSelectorState state) async {

View File

@@ -11,7 +11,7 @@ export 'null_status.dart';
export 'pop_container.dart';
export 'disabled_mask.dart';
export 'side_sheet.dart';
export 'extend_page.dart';
export 'sheet.dart';
export 'keep_container.dart';
export 'animate_grid.dart';
export 'tray_container.dart';
@@ -22,4 +22,5 @@ export 'tile_container.dart';
export 'chip.dart';
export 'fade_box.dart';
export 'app_state_container.dart';
export 'text.dart';
export 'text.dart';
export 'connection_item.dart';

View File

@@ -18,7 +18,6 @@ class WindowContainer extends StatefulWidget {
}
class _WindowContainerState extends State<WindowContainer> with WindowListener {
_autoLaunchContainer(Widget child) {
return Selector<Config, bool>(
selector: (_, config) => config.autoLaunch,
@@ -47,6 +46,28 @@ class _WindowContainerState extends State<WindowContainer> with WindowListener {
super.onWindowClose();
}
@override
Future<void> onWindowMoved() async {
super.onWindowMoved();
final offset = await windowManager.getPosition();
final config = globalState.appController.config;
config.windowProps = config.windowProps.copyWith(
top: offset.dy,
left: offset.dx,
);
}
@override
Future<void> onWindowResized() async {
super.onWindowResized();
final size = await windowManager.getSize();
final config = globalState.appController.config;
config.windowProps = config.windowProps.copyWith(
width: size.width,
height: size.height,
);
}
@override
void onWindowMinimize() async {
await globalState.appController.savePreferences();