Files
gongyun b02daf1448 主要合入内容:
- 文件管理器分页加载、排序偏好、桌面表头排序、移动端排序菜单、Ctrl/Cmd+F搜索、Esc 关闭搜索、鼠标右滑返回上级。
- 移动端同步状态页、同步统计卡片、同步任务/累计统计事件、同步页布局更新。
- Android 相册同步默认路径改为 DCIM/Camera,下载路径改用 external_path。
- Rust sync-core 更新:Android 日志依赖、AlbumUpload/AlbumDownload、MirrorWcf 统计修复、Linux FUSE镜像挂载与读写同步支持。

- 依赖更新:external_path、fl_chart、pdfrx 升级,并修了 pdfrx 新版本的deprecated API。
- 新增文件包括:sort_options.dart、sync_page_android.dart、sync_stats_card.dart、platform/fuse.rs、sync_engine/fuse.rs。
2026-06-04 07:11:43 +08:00

189 lines
5.4 KiB
Dart

import 'package:cloudreve4_flutter/presentation/providers/navigation_provider.dart';
import 'package:cloudreve4_flutter/router/app_router.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:provider/provider.dart';
class _QuickFunction {
final IconData icon;
final String label;
final String? route;
final void Function(BuildContext context)? onTap;
const _QuickFunction({
required this.icon,
required this.label,
this.route,
this.onTap,
});
}
class QuickFunctionsSection extends StatelessWidget {
const QuickFunctionsSection({super.key});
static final _functions = [
_QuickFunction(
icon: LucideIcons.share2,
label: '我的分享',
route: RouteNames.share,
),
_QuickFunction(
icon: LucideIcons.cloud,
label: 'WebDAV',
route: RouteNames.webdav,
),
_QuickFunction(
icon: LucideIcons.download,
label: '离线下载',
route: RouteNames.remoteDownload,
),
_QuickFunction(
icon: LucideIcons.trash2,
label: '回收站',
route: RouteNames.recycleBin,
),
_QuickFunction(
icon: LucideIcons.refreshCw,
label: '文件同步',
onTap: (ctx) {
final nav = ctx.read<NavigationProvider>();
// 桌面端或 Android 平板(宽屏)有同步 Tab,直接切换;手机端跳转同步详情页
final isDesktop =
defaultTargetPlatform != TargetPlatform.android &&
defaultTargetPlatform != TargetPlatform.iOS;
final isWideScreen = MediaQuery.of(ctx).size.width >= 800;
if (isDesktop || isWideScreen) {
nav.setIndex(3);
} else {
Navigator.of(ctx).pushNamed(RouteNames.syncStatus);
}
},
),
_QuickFunction(
icon: LucideIcons.settings,
label: '设置',
route: RouteNames.settings,
),
];
static const double _spacing = 12;
static const double _runSpacing = 4;
static const double _minItemWidth = 120;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(left: 4, bottom: 14),
child: Row(
children: [
Icon(LucideIcons.zap, size: 18, color: theme.colorScheme.primary),
const SizedBox(width: 8),
Text(
'快捷功能',
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
],
),
),
LayoutBuilder(
builder: (context, constraints) {
final availableWidth = constraints.maxWidth;
int perRow = 1;
while (perRow < _functions.length) {
final next = perRow + 1;
final itemWidth = (availableWidth - _spacing * (next - 1)) / next;
if (itemWidth < _minItemWidth) break;
perRow = next;
}
final itemWidth =
(availableWidth - _spacing * (perRow - 1)) / perRow;
return Wrap(
spacing: _spacing,
runSpacing: _runSpacing,
children: _functions.map((fn) {
return SizedBox(
width: itemWidth,
child: _QuickFunctionCard(
icon: fn.icon,
label: fn.label,
onTap: () {
if (fn.onTap != null) {
fn.onTap!(context);
} else if (fn.route != null) {
Navigator.of(context).pushNamed(fn.route!);
}
},
),
);
}).toList(),
);
},
),
],
);
}
}
class _QuickFunctionCard extends StatefulWidget {
final IconData icon;
final String label;
final VoidCallback onTap;
const _QuickFunctionCard({
required this.icon,
required this.label,
required this.onTap,
});
@override
State<_QuickFunctionCard> createState() => _QuickFunctionCardState();
}
class _QuickFunctionCardState extends State<_QuickFunctionCard> {
bool _hovered = false;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
return Card(
color: _hovered ? colorScheme.surfaceContainerHighest : null,
child: InkWell(
onTap: widget.onTap,
borderRadius: BorderRadius.circular(12),
onHover: (v) => setState(() => _hovered = v),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
child: Row(
children: [
Icon(widget.icon, size: 20, color: colorScheme.primary),
const SizedBox(width: 10),
Flexible(
child: Text(
widget.label,
style: theme.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w500,
color: _hovered ? colorScheme.primary : null,
),
overflow: TextOverflow.ellipsis,
),
),
],
),
),
),
);
}
}