Files
pangolin/client/lib/widgets/nav_sidebar.dart
T
wangjia 281a6261fb feat(client): tablet 外壳 + 视觉 diff 工作流文档
- tablet_shell: 侧栏 232·4 项触控(minHeight 48, 字号 15) + 顶栏无主题切换 + 连接页双栏
  对照 ui_kits/tablet/tabapp.jsx;home_shell 分发 tablet→TabletShell
- nav_sidebar 加 dense 参数(desktop 紧凑 / tablet 触控)
- content_top_bar 加 showThemeToggle + titleSize 参数
- tool/visual-diff.md: 截图 diff 工作流(design-distill 工具)+ 实现侧三路径
  现状(golden 被 google_fonts 阻塞 / screencapture 受 macOS chrome 限制)+ 推荐

注: tablet 形态在 macOS 桌面平台不触发(只 desktop/mobile),运行验证需 iPad。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 09:38:18 +08:00

112 lines
4.0 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// nav_sidebar.dart — desktop/tablet 左侧栏导航
//
// 对照 ui_kits/desktop/dapp.jsx:宽 204、bgSubtle 底 + 右边框;
// 品牌区(mark28 + 穿山甲 + PANGOLIN) → NavItem 列表 → Spacer → 套餐卡。
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../pangolin_theme.dart';
import '../state/app_providers.dart';
import '../state/navigation_provider.dart';
import 'pangolin_icons.dart';
import 'pangolin_logo.dart';
import 'plan_badge_card.dart';
typedef NavSidebarItem = ({IconData icon, String label, NavView view});
class NavSidebar extends ConsumerWidget {
const NavSidebar({super.key, required this.items, this.width = 204, this.dense = true});
final List<NavSidebarItem> items;
final double width;
/// dense=true 桌面紧凑(行高~36)dense=false 平板触控(行高≥48, 字号更大)。
final bool dense;
@override
Widget build(BuildContext context, WidgetRef ref) {
final c = context.pangolin;
final t = ref.watch(appTextProvider);
final current = ref.watch(navViewProvider);
return Container(
width: width,
decoration: BoxDecoration(
color: c.bgSubtle,
border: Border(right: BorderSide(color: c.border)),
),
padding: EdgeInsets.fromLTRB(dense ? 12 : 14, dense ? 14 : 18, dense ? 12 : 14, dense ? 14 : 16),
child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
// 品牌区
Padding(
padding: EdgeInsets.fromLTRB(6, 2, 6, dense ? 16 : 20),
child: Row(children: [
PangolinMark(size: dense ? 28 : 30),
SizedBox(width: dense ? 9 : 10),
Column(crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [
Text(t.brand,
style: PangolinText.h3.copyWith(
color: c.fg1, fontWeight: FontWeight.w700, fontSize: dense ? 16 : 17, height: 1)),
const SizedBox(height: 3),
Text('PANGOLIN',
style: PangolinText.caption.copyWith(
color: c.accent, fontWeight: FontWeight.w600, fontSize: 9, letterSpacing: 1.6)),
]),
]),
),
// Nav 列表
for (final item in items)
Padding(
padding: EdgeInsets.only(bottom: dense ? 3 : 4),
child: _NavItem(
icon: item.icon,
label: item.label,
active: current == item.view,
dense: dense,
onTap: () => ref.read(navViewProvider.notifier).state = item.view,
),
),
const Spacer(),
const PlanBadgeCard(),
]),
);
}
}
class _NavItem extends StatelessWidget {
const _NavItem({required this.icon, required this.label, required this.active, required this.onTap, this.dense = true});
final IconData icon;
final String label;
final bool active;
final VoidCallback onTap;
final bool dense;
@override
Widget build(BuildContext context) {
final c = context.pangolin;
return Material(
color: active ? c.accentSubtle : Colors.transparent,
borderRadius: BorderRadius.circular(PangolinRadius.md),
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: onTap,
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: dense ? 0 : 48),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: dense ? 12 : 14, vertical: dense ? 9 : 13),
child: Row(children: [
Icon(icon, size: dense ? 18 : 20, color: active ? c.accent : c.fg3),
SizedBox(width: dense ? 11 : 12),
Text(label,
style: PangolinText.sm.copyWith(
color: active ? c.accent : c.fg2,
fontSize: dense ? 13.5 : 15,
fontWeight: active ? (dense ? FontWeight.w600 : FontWeight.w700) : FontWeight.w500)),
]),
),
),
),
);
}
}