Files
photo-live/apps/mobile/lib/ui/components/segment_tabs.dart
T
浅念 6074fc8ed3 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>
2026-09-23 08:09:20 +08:00

119 lines
3.1 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.
/// 轻量分段选择器(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,
),
),
),
),
);
}
}