// tablet_shell.dart — 平板外壳(侧栏 4 项触控 + 内容区),对照 ui_kits/tablet/tabapp.jsx // // 与 desktop 区别:侧栏 232 触控尺寸、4 项(无联系/设置)、顶栏无主题切换; // 连接页自动走双栏(connect_page 在 formFactor==tablet 时进 isWide 分支)。 import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../pangolin_theme.dart'; import '../screens/account_page.dart'; import '../screens/connect_page.dart'; import '../screens/contact_page.dart'; import '../screens/nodes_page.dart'; import '../screens/notifications_page.dart'; import '../screens/settings_page.dart'; import '../screens/stats_page.dart'; import '../state/app_providers.dart'; import '../state/navigation_provider.dart'; import '../widgets/content_top_bar.dart'; import '../widgets/nav_sidebar.dart'; import '../widgets/pangolin_icons.dart'; import '../widgets/routing_screen.dart'; class TabletShell extends ConsumerWidget { const TabletShell({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final c = context.pangolin; final t = ref.watch(appTextProvider); final view = ref.watch(navViewProvider); // 「我的」经侧栏底部 PlanBadgeCard 进入(见 NavSidebar;品牌块本身不可点);设置/联系我们对齐桌面侧栏。 final items = [ (icon: PangolinIcons.power, label: t.tabConnect, view: NavView.connect), (icon: PangolinIcons.globe, label: t.tabServers, view: NavView.servers), (icon: PangolinIcons.chartNoAxesColumn, label: t.tabStats, view: NavView.stats), (icon: PangolinIcons.settings, label: t.settingsTitle, view: NavView.settings), (icon: PangolinIcons.messageCircle, label: t.contactTitle, view: NavView.contact), ]; final titles = { NavView.connect: t.tabConnect, NavView.servers: t.tabServers, NavView.stats: t.tabStats, NavView.account: t.tabMe, NavView.settings: t.settingsTitle, NavView.contact: t.contactTitle, NavView.invite: t.inviteTitle, NavView.notifications: t.notifTitle, NavView.routing: t.routingRulesTitle, }; void go(NavView v) => ref.read(navViewProvider.notifier).state = v; Widget content() { switch (view) { case NavView.connect: return ConnectPage(isWide: true, onOpenNodes: () => go(NavView.servers)); case NavView.servers: return NodesPage(isWide: true, onPicked: () => go(NavView.connect)); case NavView.stats: return const StatsPage(isWide: true); case NavView.settings: return const SettingsPage(); case NavView.contact: return const ContactPage(); case NavView.notifications: return NotificationsScreen(t: t, embedded: true); case NavView.routing: return const RoutingScreen(embedded: true); default: // account / plans / redeem / devices → 账户(其余下钻在账户页内) return const AccountPage(isWide: true); } } // 通知子页返回目标:回账户;设置下钻子页 → settings(镜像桌面 desktop_shell.dart::backTarget)。 VoidCallback? backTarget() { if (view == NavView.notifications) return () => go(NavView.account); if (kSettingsSubViews.contains(view)) return () => go(NavView.settings); return null; } return ColoredBox( color: c.bg, // iPad 上内容会顶到状态栏(时间/电量/notch)。SafeArea 加顶部安全区,避开重叠; // 底部留给页面自行处理(home indicator)。 child: SafeArea( bottom: false, child: Row(children: [ NavSidebar(items: items, width: 232, dense: false), Expanded( child: Column(children: [ ContentTopBar( title: titles[view] ?? t.tabMe, onBack: backTarget(), onBell: () => go(NavView.notifications), showThemeToggle: false, titleSize: 19, ), Expanded(child: content()), ]), ), ]), ), ); } }