Files
pangolin/client/lib/widgets/content_top_bar.dart
T
wangjia 4dc4127252 feat(client): 三端布局架构 + macOS 桌面端 + app 图标
三端布局(mobile/tablet/desktop):
- core/responsive/form_factor.dart 形态判定 + shell/ 分发器(home_shell→desktop/mobile)
- desktop_shell 对照 ui_kits/desktop/dapp.jsx: 侧栏204·6项 + 套餐卡 + 顶栏(标题/状态/主题切换) + 连接页居中单列
- 新增组件 nav_sidebar / plan_badge_card / content_top_bar / bottom_tab_bar
- 新增一级页 contact_page / settings_page; navigation_provider(NavView)
- 删除旧 widgets/home_shell.dart(逻辑迁入 shell/)

macOS 桌面端:
- 窗口默认 920×600 + 最小 720×560(MainFlutterWindow.swift)
- app 图标替换为穿山甲(AppIcon.appiconset 全套, 由 app-icon.svg 渲染)

其余(本会话):
- Phase2 接线: auth_api/token_store/auth_provider/vpn_bridge_provider + 真实 connection/nodes
- lucide_icons 兼容补丁(packages/lucide_icons_patched) 修复 IconData final 报错
- 测试修复: connect_passthrough(UTF-8) / harness / golden @Skip
- l10n 新增 settingsTitle

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

67 lines
2.4 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.
// content_top_bar.dart — desktop/tablet 内容区顶栏(标题 + 在线状态 + 主题切换)
//
// 对照 ui_kits/desktop/dapp.jsxheight 52、下边框;左标题(display 17)
// 右「● 已连接 CODE / ○ 未连接」(mono 12) + moon/sun 主题切换。
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../pangolin_theme.dart';
import '../state/app_providers.dart';
import '../state/connection_provider.dart';
import '../state/nodes_provider.dart';
import 'pangolin_icons.dart';
class ContentTopBar extends ConsumerWidget {
const ContentTopBar({super.key, required this.title, this.onBack});
final String title;
final VoidCallback? onBack;
@override
Widget build(BuildContext context, WidgetRef ref) {
final c = context.pangolin;
final conn = ref.watch(connectionProvider);
final node = ref.watch(effectiveNodeProvider);
final mode = ref.watch(themeModeProvider);
final isDark = mode == ThemeMode.dark;
return Container(
height: 52,
padding: const EdgeInsets.symmetric(horizontal: 24),
decoration: BoxDecoration(border: Border(bottom: BorderSide(color: c.border))),
child: Row(children: [
if (onBack != null) ...[
IconButton(
onPressed: onBack,
icon: Icon(PangolinIcons.arrowLeft, size: 20, color: c.fg1),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(minWidth: 32, minHeight: 32),
),
const SizedBox(width: 4),
],
Text(title,
style: PangolinText.h3.copyWith(color: c.fg1, fontWeight: FontWeight.w700, fontSize: 17)),
const Spacer(),
Text(
conn.phase == VpnPhase.on ? '${node.code}' : '',
style: PangolinText.mono.copyWith(
fontSize: 12,
fontWeight: FontWeight.w600,
color: conn.phase == VpnPhase.on ? c.success : c.fg3,
),
),
const SizedBox(width: 12),
InkWell(
onTap: () => ref.read(themeModeProvider.notifier).state =
isDark ? ThemeMode.light : ThemeMode.dark,
borderRadius: BorderRadius.circular(PangolinRadius.sm),
child: Padding(
padding: const EdgeInsets.all(6),
child: Icon(isDark ? PangolinIcons.sun : PangolinIcons.moon, size: 18, color: c.fg2),
),
),
]),
);
}
}