feat(ui06): App line A0-A7 — nav pill removal, light segments, bare summary, token alignment to app-preview

- tokens:次文字 #69778E、soft/card-border/seg 色组、输入圆角 10(kRadiusField)
- theme:导航 indicatorColor 透明+显式选中蓝;卡片去阴影改 1px #EDF0F5 边框;输入边框同步
- 新组件 SegmentTabs(#EDF1F6 轨道/选中实蓝/light 浅蓝变体/null 无高亮)替换 M3 SegmentedButton
- 上传中心:摘要去白卡直排+主数字 32/40;completed 次级筛选无高亮语义保留
- 相册卡 scrim 30% 起 #0C1728E8;导航角标红底白字白边;登录主标题 27/700
- theme_test 三处旧规格断言随 UI06 值更新;397/397 全绿、analyze 0 issues
- 真机证据:MuMu + 测试服务器 API,登录/相册/详情/上传 4 页截图确认达标
- 登记偏差:语义色保持对比度合规深色版;视口矩阵随 G2;详见验收记录 §6

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-23 08:09:20 +08:00
parent 8dc0073597
commit 6074fc8ed3
13 changed files with 320 additions and 141 deletions
@@ -0,0 +1,118 @@
/// 轻量分段选择器(UI06 对齐 app-preview .tabs)。
///
/// 形态:#EDF1F6 轨道、padding 4、整体圆角 9;选中实底、未选次文字色。
/// 两种选中风格:
/// - [SegmentTabsStyle.solid]:主蓝实底白字(上传中心筛选等状态筛选);
/// - [SegmentTabsStyle.light]:#DBEAFF 底 + #145BB9 字(Details/Access 类
/// 内容页签——与状态筛选区分,见 app-preview .tabs.light)。
///
/// 替换默认 [SegmentedButton](M3 黑边胶囊形态与示例不符)。
library;
import 'package:flutter/material.dart';
import '../tokens.dart';
enum SegmentTabsStyle { solid, light }
class SegmentTabs<T> extends StatelessWidget {
const SegmentTabs({
required this.value,
required this.onChanged,
required this.segments,
this.style = SegmentTabsStyle.solid,
super.key,
});
/// 当前值;null = 无高亮(次级筛选接管时的中间态)。
final T? value;
final ValueChanged<T> onChanged;
/// 选项:值 + 本地化标签。
final List<(T, String)> segments;
final SegmentTabsStyle style;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final light = style == SegmentTabsStyle.light;
return Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: kColorSegTrack,
borderRadius: BorderRadius.circular(9),
),
child: Row(
children: [
for (final (v, label) in segments)
Expanded(
child: _TabButton(
label: label,
selected: v == value,
light: light,
primary: theme.colorScheme.primary,
onTap: () => onChanged(v),
),
),
],
),
);
}
}
class _TabButton extends StatelessWidget {
const _TabButton({
required this.label,
required this.selected,
required this.light,
required this.primary,
required this.onTap,
});
final String label;
final bool selected;
final bool light;
final Color primary;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
final Color bg;
final Color fg;
if (selected) {
bg = light ? kColorSegLightActive : primary;
fg = light ? kColorSegLightActiveText : Colors.white;
} else {
bg = Colors.transparent;
fg = kColorTextSecondary;
}
return Semantics(
button: true,
selected: selected,
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(7),
child: Container(
alignment: Alignment.center,
height: 36,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 5),
decoration: BoxDecoration(
color: bg,
borderRadius: BorderRadius.circular(7),
),
child: Text(
label,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 12,
fontWeight: selected ? FontWeight.w600 : FontWeight.w500,
color: fg,
),
),
),
),
);
}
}
+20 -3
View File
@@ -29,8 +29,24 @@ const Color kColorInfo = Color(0xFF1A73E8);
/// 分隔线:轻分隔,不用粗边框(#E5EAF1)。
const Color kColorDivider = Color(0xFFE5EAF1);
/// 次文字:时间、地点、说明(#5E6878)。
const Color kColorTextSecondary = Color(0xFF5E6878);
/// 次文字:时间、地点、说明(app-preview --muted #69778E)。
const Color kColorTextSecondary = Color(0xFF69778E);
/// 浅蓝软底(app-preview --soft #EDF4FF):信息提示、tag 底、选中页签。
const Color kColorSoft = Color(0xFFEDF4FF);
/// 卡片细边框(app-preview .card/.group border #EDF0F5;卡为边框形态非阴影)。
const Color kColorCardBorder = Color(0xFFEDF0F5);
/// 分段轨道底(app-preview .tabs #EDF1F6)。
const Color kColorSegTrack = Color(0xFFEDF1F6);
/// 输入框默认边框(app-preview .field input #DBE3EF)。
const Color kColorFieldBorder = Color(0xFFDBE3EF);
/// 浅蓝选中分段(app-preview .tabs.light active:#DBEAFF 底 + #145BB9 字)。
const Color kColorSegLightActive = Color(0xFFDBEAFF);
const Color kColorSegLightActiveText = Color(0xFF145BB9);
// ── 语义色(深色变体:深色表面对应提亮值)──────────────────────────────
@@ -64,10 +80,11 @@ const double kSpace16 = 16.0;
const double kSpace24 = 24.0;
const double kSpace32 = 32.0;
// ── 圆角(卡片 16 / 输入框与按钮 12 / 小标签 8)────────────────────────
// ── 圆角(卡片 16 / 按钮 12 / 输入框 10 / 小标签 8)──────────────────────
const double kRadiusCard = 16.0;
const double kRadiusInput = 12.0;
const double kRadiusField = 10.0;
const double kRadiusBadge = 8.0;
// ── 动效时长(轻量 150-220ms / 轮播 250-350ms)────────────────────────