1. 新增工作台三栏布局模式,适配宽屏设备 2. 添加跨平台系统托盘支持,新增托盘图标资源 3. 修复工作台模式下导航返回异常问题 4. 统一JSON类型安全解析,替换硬类型转换 5. 增加macOS深度链接支持,统一渠道分发信息 6. 优化部分页面生命周期和状态加载逻辑 7. 移除废弃的nearby_connections依赖
127 lines
4.4 KiB
Dart
127 lines
4.4 KiB
Dart
/// ============================================================
|
||
/// 闲言APP — 自适应分屏组件
|
||
/// 创建时间: 2026-05-29
|
||
/// 更新时间: 2026-06-18
|
||
/// 作用: 宽屏时左右分屏布局,支持可拖拽分割线、手势隔离、动画过渡
|
||
/// 上次更新: 断点常量对齐设计规则 768/1024/1280,支持工作台三栏模式
|
||
/// ============================================================
|
||
|
||
import 'package:flutter/cupertino.dart';
|
||
import 'package:flutter_animate/flutter_animate.dart';
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
|
||
import '../providers/split_view_provider.dart';
|
||
import 'panel_cache.dart';
|
||
import 'split_divider.dart';
|
||
|
||
// ============================================================
|
||
// 响应式断点常量 — 对齐 .trae/rules/design-rules.md
|
||
// ============================================================
|
||
|
||
/// 紧凑模式断点:宽度 >= 768px 从单栏切换到双栏
|
||
const double kCompactBreakpoint = 768.0;
|
||
|
||
/// 中等模式断点:宽度 >= 1024px 从双栏切换到三栏
|
||
const double kMediumBreakpoint = 1024.0;
|
||
|
||
/// 展开模式断点:宽度 >= 1280px 三栏完整显示(中栏更宽)
|
||
const double kExpandedBreakpoint = 1280.0;
|
||
|
||
/// @deprecated 旧断点,保留向后兼容,实际指向 kCompactBreakpoint
|
||
const double kSplitViewBreakpoint = kCompactBreakpoint;
|
||
|
||
/// 判断当前宽度是否处于工作台模式(双栏或三栏)
|
||
bool isWorkbenchWidth(double width) => width >= kCompactBreakpoint;
|
||
|
||
/// 判断当前宽度是否处于三栏模式
|
||
bool isTripleColumnWidth(double width) => width >= kMediumBreakpoint;
|
||
|
||
/// 判断当前宽度是否处于三栏完整模式(中栏更宽)
|
||
bool isExpandedTripleColumnWidth(double width) => width >= kExpandedBreakpoint;
|
||
|
||
class AdaptiveSplitView extends ConsumerStatefulWidget {
|
||
const AdaptiveSplitView({
|
||
required this.leftPanel,
|
||
required this.rightPanel,
|
||
this.minLeftWidth = 320,
|
||
this.minRightWidth = 400,
|
||
super.key,
|
||
});
|
||
|
||
final Widget leftPanel;
|
||
final Widget rightPanel;
|
||
final double minLeftWidth;
|
||
final double minRightWidth;
|
||
|
||
@override
|
||
ConsumerState<AdaptiveSplitView> createState() => _AdaptiveSplitViewState();
|
||
}
|
||
|
||
class _AdaptiveSplitViewState extends ConsumerState<AdaptiveSplitView> {
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final screenWidth = MediaQuery.sizeOf(context).width;
|
||
final splitState = ref.watch(splitViewProvider);
|
||
final isSplitView =
|
||
screenWidth >= kSplitViewBreakpoint && splitState.splitViewEnabled;
|
||
final splitRatio = splitState.splitRatio;
|
||
final hasRightContent = splitState.activeRightPanel != null;
|
||
|
||
if (!isSplitView) {
|
||
return widget.leftPanel;
|
||
}
|
||
|
||
final leftWidth = screenWidth * splitRatio;
|
||
const double dividerWidth = 17.0;
|
||
final double clampedLeftWidth = leftWidth.clamp(
|
||
widget.minLeftWidth,
|
||
screenWidth - widget.minRightWidth - dividerWidth,
|
||
);
|
||
|
||
return Row(
|
||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||
children: [
|
||
SizedBox(
|
||
width: clampedLeftWidth,
|
||
child: KeepAlivePanelWrapper(
|
||
child: NotificationListener<ScrollNotification>(
|
||
onNotification: (_) => true,
|
||
child: widget.leftPanel,
|
||
),
|
||
),
|
||
),
|
||
SplitDivider(
|
||
currentPosition: splitRatio,
|
||
onPositionChanged: (newRatio) {
|
||
ref.read(splitViewProvider.notifier).setSplitRatio(newRatio);
|
||
},
|
||
minPosition: widget.minLeftWidth / screenWidth,
|
||
maxPosition:
|
||
(screenWidth - widget.minRightWidth - dividerWidth) / screenWidth,
|
||
),
|
||
SizedBox(
|
||
width: (screenWidth - clampedLeftWidth - dividerWidth).clamp(
|
||
widget.minRightWidth,
|
||
screenWidth - widget.minLeftWidth - dividerWidth,
|
||
),
|
||
child: KeepAlivePanelWrapper(
|
||
child:
|
||
NotificationListener<ScrollNotification>(
|
||
onNotification: (_) => true,
|
||
child: widget.rightPanel,
|
||
)
|
||
.animate(target: hasRightContent ? 1.0 : 0.0)
|
||
.slideX(
|
||
begin: 1.0,
|
||
end: 0.0,
|
||
duration: 350.ms,
|
||
curve: Curves.easeInOutCubic,
|
||
)
|
||
.fadeIn(duration: 200.ms),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
}
|