209021e7d2
- 新增 development/iterations/iteration-1/:15 份角色报告 + 进展看板(已完成/未闭环/下一步),作为双人协作的进度事实来源 - 新增 ADR-006:测试与交付容器化策略(Testcontainers / 交付 Docker 包 / 本机库仅个人联调) - Git 工作流规范补充:敏感信息只进忽略文件或 sample、测试数据不入库、测试代码限标准测试目录 - 门禁:mkdocs build --strict 通过(零警告)
347 lines
23 KiB
Markdown
347 lines
23 KiB
Markdown
# Patbond 第一迭代 · 登录/注册 UI 设计规范
|
||
|
||
> 作者:UI Designer
|
||
> 日期:2026-09-03
|
||
> 迭代:Iteration 1「真实登录纵切」(development-plan.md §7 M1、§8)
|
||
> 素材来源:`AI宠物_iOS_UI设计稿.html`(视觉语言)、`patbond-flutter/lib/`(已实现主题与组件)、`patbond-doc/docs/development/development-plan.md`(§6.2 接口、§7 M1、§9/§10 质量门禁)
|
||
|
||
---
|
||
|
||
## 0. 关键前提:两套视觉语言的分歧与决策
|
||
|
||
现有素材存在一个必须先决策的分歧:
|
||
|
||
| 来源 | 主色 | 底色 | 字体 | 定位 |
|
||
| --- | --- | --- | --- | --- |
|
||
| HTML 设计稿(正式设计交付物) | 珊瑚橙 `#FF6F4C` 暖色系 | 奶油色 `#FFF7ED` | Baloo 2 标题 + Inter 正文 | 品牌方向 |
|
||
| Flutter `app_theme.dart`(当前实现) | 靛蓝 `#4F46E5` | 冷灰 `#F8FAFC` | 系统字体 + CJK fallback | 脚手架占位 |
|
||
|
||
**决策:以 HTML 设计稿的暖色系为品牌正典(canonical)。** 理由:设计稿是明确的品牌交付物,宠物社区产品的暖色调是刻意的情感设计;而 Flutter 的靛蓝主题是典型的模板默认色。登录页是用户接触产品的第一屏,应当承载品牌。
|
||
|
||
**落地策略(降低迁移风险):** 本规范全部使用语义 token 命名(`primary`、`surface`、`ink`…),与 `AppColors` 现有字段一一对应。主题集中在 `app_theme.dart` 单文件,重映射色值即可全局切换。若团队决定第一迭代不动主题,本规范的布局、组件、状态定义在靛蓝主题下同样成立,仅色值不同——两种情况都不需要改登录页代码。
|
||
|
||
---
|
||
|
||
## 1. 现有设计系统提炼
|
||
|
||
### 1.1 色板(语义 token → 暖色正典值 / 现有 Flutter 值)
|
||
|
||
| Token | 暖色正典(设计稿) | 现有 Flutter | 用途 |
|
||
| --- | --- | --- | --- |
|
||
| `primary` | `#FF6F4C` 珊瑚橙 | `#4F46E5` | 品牌色、图标、装饰、渐变起点 |
|
||
| `primaryStrong` | `#D6431A`(新增,加深珊瑚) | — | 实心按钮填充、可点击文字链接。白字对比度约 4.5:1,满足 WCAG AA;`#FF6F4C` 白字仅 2.75:1,不得用于承载文字的实心填充 |
|
||
| `primaryDark` | `#7A2E12` coral-dark | — | 浅色底上的强调文字、按钮反白替代 |
|
||
| `accent` | `#FFB648` amber | — | 渐变终点、徽章、会员/促销 CTA |
|
||
| `accentDark` | `#7A4B0A` amber-dark | — | amber 底上的文字 |
|
||
| `canvas` | `#FFF7ED` bg-cream | `#F8FAFC` | 页面背景 |
|
||
| `surface` | `#FFFFFF` | `#FFFFFF` | 卡片、输入框背景 |
|
||
| `surfaceTint` | `#FFE8D6` peach | `#E0E7FF` | 图标底、占位块、选中指示 |
|
||
| `ink` | `#3E2A1F` | `#0F172A` | 主文字 |
|
||
| `muted` | `#9C8977` | `#64748B` | 次级文字、占位符 |
|
||
| `border` | `#F0DCC8` | `#E2E8F0` | 描边、分隔线 |
|
||
| `success` | `#7FA88A` sage(文字用 `#3F5744`,底用 `#E8F0E8`) | `#10B981` | 成功提示 |
|
||
| `warning` | `#F59E0B` | `#F59E0B` | 警告 |
|
||
| `error` | `#D0342C`(新增;设计稿与主题均缺失) | — | 校验错误文字、错误描边、失败提示。白底上约 5.4:1,AA 达标 |
|
||
| `brandGradient` | `linear-gradient(135deg, #FF6F4C, #FFB648)` | — | 品牌 hero 区、头像环、装饰性大面积(不承载正文文字) |
|
||
|
||
### 1.2 字号层级
|
||
|
||
设计稿是 320px 画框内的缩样(9–19px),不能按像素照搬;Flutter `textTheme` 已是放大到真机的合理映射,作为基准沿用:
|
||
|
||
| 层级 | 字号/字重 | 对应 | 登录页用途 |
|
||
| --- | --- | --- | --- |
|
||
| Display(品牌字标) | 32 / w700,标题字体(Baloo 2 或圆润中文标题体,可后置) | 设计稿 `.logo` 放大 | 登录页 "Patbond" 字标 |
|
||
| `headlineSmall` | 22 / w800 | 已有 | 页面标题("欢迎回来"/"创建账号") |
|
||
| `titleLarge` | 18 / w800 | 已有 | 分区标题 |
|
||
| `titleMedium` | 15 / w700 | 已有 | 按钮文字、表单 label |
|
||
| `bodyMedium` | 14 / 1.5 行高 | 已有 | 正文、输入内容(输入框内建议 16 防 iOS 缩放) |
|
||
| `bodySmall` | 12 / 1.4 行高,muted 色 | 已有 | 辅助说明、协议文案 |
|
||
| Caption | 11 / w700 | TagPill 已有 | 徽章、错误行(错误行用 12) |
|
||
|
||
### 1.3 圆角
|
||
|
||
设计稿圆角层级:芯片胶囊 999 > hero 20 > 卡片 16–18 > 输入框/小卡 14 > 按钮 10–12。Flutter 现值:Card 24、Input 18、SnackBar 16。取两者交集定标准:
|
||
|
||
| Token | 值 | 用途 |
|
||
| --- | --- | --- |
|
||
| `radiusSm` | 12 | 小按钮、内嵌 CTA |
|
||
| `radiusMd` | 16 | 主按钮、SnackBar、菜单组 |
|
||
| `radiusLg` | 18 | 输入框(沿用 `inputDecorationTheme` 现值)、feed 卡 |
|
||
| `radiusXl` | 24 | 大卡片(沿用 `cardTheme` 现值) |
|
||
| `radiusPill` | 999 | 芯片、徽章 |
|
||
|
||
### 1.4 间距
|
||
|
||
基数 4。常用刻度:4 / 8 / 12 / 16 / 24 / 32 / 48。页面水平留白 16(现有页面 `EdgeInsets.fromLTRB(16, 16, 16, 30)` 一致),卡片内边距 18–24(`SectionCard` 默认 18)。
|
||
|
||
### 1.5 既有组件风格基线
|
||
|
||
- **输入框**(`inputDecorationTheme` 已定义,直接复用):白底 filled,圆角 18,内边距 H16/V14,默认描边 `border` 1px,聚焦描边 `primary` 1.5px。
|
||
- **按钮**:现有页面用 Material 3 `FilledButton` / `OutlinedButton` / `TextButton`,未做全局主题化——本迭代补齐(见 §6)。
|
||
- **卡片**:白底、1px 极浅描边、零 elevation(阴影极克制,与设计稿一致)。
|
||
- **已有可复用组件**(`lib/widgets/common.dart`):`RemoteImage`(loading/error 兜底)、`SectionCard`、`TagPill`、`EmptyState`。
|
||
- **加载**:`CircularProgressIndicator`(主壳启动、创作页、档案页已用)。
|
||
|
||
---
|
||
|
||
## 2. 登录页规范
|
||
|
||
### 2.1 布局结构
|
||
|
||
单列居中布局,无 AppBar,无底部 TabBar。页面背景 `canvas`,可在顶部叠加一层由 `surfaceTint` 到透明的极浅径向渐变作氛围(可选装饰,非必需)。
|
||
|
||
```text
|
||
┌──────────────────────────────────────┐
|
||
│ SafeArea + SingleChildScrollView │ 键盘弹出时可滚动,防溢出
|
||
│ 水平 padding 24 │
|
||
│ │
|
||
│ ↑ 弹性空间 (flex, min 48) │
|
||
│ │
|
||
│ [ 品牌区 ] │
|
||
│ 🐾 logo 图形 72×72 │ v1 可用 Icons.pets 于
|
||
│ Patbond │ brandGradient 圆底(radiusXl)代替
|
||
│ 和毛孩子在一起的每一天 │ 字标 32/w700 primary;
|
||
│ │ slogan 14 muted,居中
|
||
│ 间距 48 │
|
||
│ │
|
||
│ ┌────────────────────────────────┐ │
|
||
│ │ 用户名 / 手机号 │ │ 输入框①,高 52
|
||
│ └────────────────────────────────┘ │
|
||
│ 间距 16 │
|
||
│ ┌────────────────────────────────┐ │
|
||
│ │ 密码 [👁] │ │ 输入框②,高 52,可见性切换
|
||
│ └────────────────────────────────┘ │
|
||
│ ⚠ 字段级错误显示在对应输入框下方 │
|
||
│ │
|
||
│ ┌ 表单级错误横幅(条件显示)───────┐ │ 见 §4.3
|
||
│ └────────────────────────────────┘ │
|
||
│ 间距 24 │
|
||
│ ┌────────────────────────────────┐ │
|
||
│ │ 登 录 │ │ 主按钮,高 52,全宽
|
||
│ └────────────────────────────────┘ │
|
||
│ 间距 16 │
|
||
│ 还没有账号? 立即注册 │ 行内文字链接,居中
|
||
│ │
|
||
│ ↑ 弹性空间 │
|
||
│ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ │
|
||
│ ┆ [预留区] 其他登录方式 ┆ │ v1 不渲染,见 2.4
|
||
│ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ │
|
||
│ 登录即代表同意《用户协议》《隐私政策》 │ bodySmall muted,底部 24
|
||
└──────────────────────────────────────┘
|
||
```
|
||
|
||
### 2.2 组件清单
|
||
|
||
| 组件 | 规格 |
|
||
| --- | --- |
|
||
| 品牌区 | logo 72×72;字标 32/w700 `primary`;slogan 14 `muted`;整体居中 |
|
||
| 账号输入框 | label "用户名 / 手机号",`keyboardType: text`,`textInputAction: next`,`AutofillHints.username`,前缀图标 `person_outline_rounded`(`muted` 色) |
|
||
| 密码输入框 | label "密码",`obscureText` 默认开,后缀 `visibility_off/visibility` 切换按钮(点击目标 ≥44×44),`textInputAction: done`(提交表单),`AutofillHints.password`,前缀图标 `lock_outline_rounded` |
|
||
| 主按钮「登录」 | 全宽 × 52 高,圆角 `radiusMd`(16),填充 `primaryStrong`,文字白色 15/w700。状态见 §4.4 |
|
||
| 注册入口 | "还没有账号?"(`muted`)+ "立即注册"(`primaryStrong`/w700,点击目标 ≥44 高),push 到注册页 |
|
||
| 协议行 | bodySmall,链接词 `primaryStrong`;v1 若协议页未就绪可先不渲染整行 |
|
||
|
||
### 2.3 表单字段与校验规则
|
||
|
||
| 字段 | 客户端校验(失焦 + 提交时) | 错误文案 |
|
||
| --- | --- | --- |
|
||
| 用户名/手机号 | 非空;去首尾空格 | "请输入用户名或手机号" |
|
||
| 密码 | 非空 | "请输入密码" |
|
||
|
||
登录页刻意不做格式强校验(用户名还是手机号由服务端判定),失败统一走服务端错误映射(§4.2)。
|
||
|
||
### 2.4 预留区(未确认功能,v1 不实现)
|
||
|
||
开发计划 §12 明确:手机号短信验证码登录、第三方登录**尚未确认**。设计上预留、代码上不渲染:
|
||
|
||
- **位置**:主按钮与协议行之间。展开后结构为:分隔线 + 居中文字"其他登录方式"(12 `muted`)+ 一排 44×44 圆形图标按钮(白底、`border` 描边),间距 24。
|
||
- **短信验证码**:确认后以账号输入框上方的"密码登录 / 验证码登录"分段切换(SegmentedButton 或双 Tab 文字)接入,不改变整体布局。
|
||
- 布局采用居中弹性结构,预留区展开不会挤压表单——实现时无需为此留白占位。
|
||
|
||
---
|
||
|
||
## 3. 注册页规范
|
||
|
||
### 3.1 布局结构
|
||
|
||
从登录页 push 进入,有返回能力。与登录页同一视觉框架,改为顶部对齐(字段多,不做垂直居中)。
|
||
|
||
```text
|
||
┌──────────────────────────────────────┐
|
||
│ ← 返回(AppBar 透明,仅返回箭头) │
|
||
│ SafeArea + Scroll,水平 padding 24 │
|
||
│ │
|
||
│ 创建账号 │ headlineSmall 22/w800
|
||
│ 加入 Patbond,记录毛孩子的每一天 │ bodySmall muted,间距 8
|
||
│ 间距 32 │
|
||
│ ┌────────────────────────────────┐ │
|
||
│ │ 用户名 │ │
|
||
│ └────────────────────────────────┘ │
|
||
│ 间距 16 │
|
||
│ ┌────────────────────────────────┐ │
|
||
│ │ 手机号 │ │
|
||
│ └────────────────────────────────┘ │
|
||
│ ┄┄ [预留] 短信验证码行,v1 不渲染 ┄┄ │
|
||
│ 间距 16 │
|
||
│ ┌────────────────────────────────┐ │
|
||
│ │ 密码 [👁] │ │
|
||
│ └────────────────────────────────┘ │
|
||
│ 密码 8–32 位,需包含字母和数字 │ helperText 12 muted
|
||
│ 间距 16 │
|
||
│ ┌────────────────────────────────┐ │
|
||
│ │ 确认密码 [👁] │ │
|
||
│ └────────────────────────────────┘ │
|
||
│ 间距 32 │
|
||
│ ┌────────────────────────────────┐ │
|
||
│ │ 注 册 │ │ 主按钮同登录页
|
||
│ └────────────────────────────────┘ │
|
||
│ 间距 16 │
|
||
│ 已有账号? 直接登录 │ 返回登录页(pop)
|
||
└──────────────────────────────────────┘
|
||
```
|
||
|
||
### 3.2 字段与校验规则
|
||
|
||
| 字段 | 校验(失焦即校验,提交再总校验) | 错误文案 |
|
||
| --- | --- | --- |
|
||
| 用户名 | 非空;3–20 字符;字母/数字/下划线,字母开头(最终以 API 契约为准) | "请输入用户名" / "用户名需 3–20 位,字母开头,可含数字和下划线" |
|
||
| 手机号 | 非空;11 位大陆手机号 `^1\d{10}$` | "请输入手机号" / "请输入正确的 11 位手机号" |
|
||
| 密码 | 8–32 位,含字母和数字(最终以 API 契约为准);helperText 常显规则,出错时被 errorText 替换 | "密码需 8–32 位,且同时包含字母和数字" |
|
||
| 确认密码 | 与密码一致 | "两次输入的密码不一致" |
|
||
|
||
服务端唯一性冲突(M1 已列入范围)映射回字段:用户名已存在 → 用户名字段 errorText "该用户名已被使用";手机号已注册 → 手机号字段 "该手机号已注册,可直接登录",并可附带"去登录"文字链接。
|
||
|
||
注册成功即建立会话(`POST /auth/register` 注册并创建会话),直接进入首页,不回登录页重新登录。
|
||
|
||
### 3.3 预留区
|
||
|
||
- **短信验证码**(未确认):手机号下方一行——验证码输入框(flex)+ 右侧"获取验证码"次级按钮(`OutlinedButton`,高 52,倒计时态显示"59s 后重发"并禁用)。
|
||
- 第三方登录预留区同登录页 §2.4。
|
||
|
||
---
|
||
|
||
## 4. 状态设计(对应 DoD:loading / error / retry;登录表单无 empty 态)
|
||
|
||
### 4.1 输入框状态
|
||
|
||
| 状态 | 视觉 |
|
||
| --- | --- |
|
||
| 默认 | 白底,`border` 1px 描边 |
|
||
| 聚焦 | `primary` 1.5px 描边(主题已有) |
|
||
| 错误 | `error` 1.5px 描边 + 输入框下方 errorText(12,`error` 色);聚焦重新输入后即清除该字段错误 |
|
||
| 禁用(提交中) | 整体 60% 不透明度,不可编辑 |
|
||
|
||
错误出现/消失会引起 8–20px 高度变化,可接受;不使用固定高度错误占位(多字段表单会过度稀疏)。
|
||
|
||
### 4.2 错误展示的层级策略
|
||
|
||
1. **字段级**(首选):能定位到具体字段的错误一律放该字段 errorText——包括本地校验和服务端 409/422 映射。
|
||
2. **表单级**:无法归属单一字段的业务错误(如 401 "用户名或密码错误"、429 "尝试次数过多,请稍后再试")→ 主按钮上方的行内错误横幅:`error` 8% 透明度底、`radiusSm` 圆角、内边距 12、左侧 `error_outline` 图标 18 + 文字 13 `error` 色。用横幅而非 SnackBar,因为错误需要停留在表单上下文里供用户对照修改。
|
||
3. **瞬态/网络错误**:请求超时、断网 → floating SnackBar(主题已有圆角 16):"网络异常,请检查网络后重试",附 action "重试"(重放上次提交)。
|
||
|
||
所有错误文案说人话、给出路,不透传服务端异常文本(契约规范 §6.1 禁止只返回异常文本,客户端同样禁止直接展示错误码)。
|
||
|
||
### 4.3 提交 loading 态
|
||
|
||
- 主按钮:文字替换为 20×20 白色 `CircularProgressIndicator`(strokeWidth 2.5),按钮尺寸不变、不可再点。
|
||
- 两个输入框、注册/登录切换链接同时禁用,防止请求飞行中改动或重复导航。
|
||
- 不用全屏遮罩 loading——登录请求是单按钮动作,局部 loading 干扰最小。
|
||
|
||
### 4.4 主按钮状态汇总
|
||
|
||
| 状态 | 视觉 |
|
||
| --- | --- |
|
||
| 默认 | `primaryStrong` 填充,白字 15/w700 |
|
||
| 按下 | 填充加深 8%(Material ripple 默认即可) |
|
||
| 禁用 | 主题 disabled(onSurface 12% 底 / 38% 字)。仅在提交中禁用;**不做"表单没填完就置灰"**——允许点击后给出校验错误,比让用户猜为什么按钮是灰的更友好 |
|
||
| loading | 见 §4.3 |
|
||
|
||
---
|
||
|
||
## 5. 启动过渡:登录态恢复(Splash)
|
||
|
||
### 5.1 流程
|
||
|
||
```text
|
||
App 启动
|
||
└─ Splash 展示(最短 500ms,避免闪跳)
|
||
同时并行:从安全存储读 refresh token
|
||
├─ 无 token ────────────────→ 淡入登录页
|
||
├─ 有 token → POST /auth/refresh(客户端超时 5s)
|
||
│ ├─ 成功(拿到新 access/refresh)→ 淡入首页(MainShell)
|
||
│ ├─ 401/会话失效 → 清除本地凭证 → 淡入登录页
|
||
│ └─ 网络错误/超时 → Splash 切换为错误态(见 5.3)
|
||
```
|
||
|
||
### 5.2 Splash 视觉
|
||
|
||
- 全屏 `canvas` 底色,品牌区(同登录页 §2.2:logo 72 + 字标 + slogan)垂直水平居中。
|
||
- 品牌区下方 32 处放 20×20 `CircularProgressIndicator`(`primary` 色,strokeWidth 2.5)——**仅当等待超过 300ms 才显示**,快速路径下用户只看到一闪而过的品牌屏。
|
||
- 页面切换用 300ms fade(`PageRouteBuilder` + `FadeTransition`);Splash 与登录页品牌区位置刻意一致,淡入登录页时品牌区视觉上原地不动、表单浮现,过渡自然。
|
||
- 原生层(iOS LaunchScreen / Android launch theme)应配同色 `canvas` 纯色底,避免白屏→Splash 的颜色跳变(可延后到 M6 商店配置一并做)。
|
||
|
||
### 5.3 Splash 错误态(网络失败且本地有 token 时)
|
||
|
||
不能让用户卡在无限转圈:
|
||
|
||
```text
|
||
🐾 Patbond(品牌区不动)
|
||
网络连接失败,无法恢复登录
|
||
┌──────────────┐
|
||
│ 重试 │ 次级按钮 OutlinedButton,高 44
|
||
└──────────────┘
|
||
改用账号登录 文字链接 → 清除凭证进登录页
|
||
```
|
||
|
||
「重试」重新发起 refresh;「改用账号登录」是逃生通道。**注意**:网络失败(非 401)不得自动清除本地 refresh token——只有服务端明确判定会话无效才清除。
|
||
|
||
---
|
||
|
||
## 6. Flutter 实现建议
|
||
|
||
### 6.1 复用现有主题 token
|
||
|
||
| 现有资产 | 用法 |
|
||
| --- | --- |
|
||
| `AppColors.*` | 全部沿用;**新增** `primaryStrong`、`primaryDark`、`accent`、`surfaceTint`、`error`(`error` 是本迭代硬需求,其余配合暖色迁移时加) |
|
||
| `inputDecorationTheme` | 直接满足输入框默认/聚焦态;错误态补 `errorBorder` / `focusedErrorBorder`(`AppColors.error`,1.5px)和 `errorStyle`(12px) |
|
||
| `textTheme` | 页面标题 `headlineSmall`、按钮/label `titleMedium`、辅助文字 `bodySmall` |
|
||
| `snackBarTheme` | 网络错误提示直接用 |
|
||
| `EmptyState`(common.dart) | 登录流用不到,但其"图标+文案"模式是 Splash 错误态的参照 |
|
||
|
||
建议在 `buildAppTheme()` 补充 `filledButtonTheme`:`minimumSize: Size.fromHeight(52)`、`RoundedRectangleBorder(borderRadius: BorderRadius.circular(16))`、`textStyle: 15/w700`——一次定义,登录/注册/后续所有主操作按钮受益。若采纳暖色迁移,同时把 `ColorScheme.fromSeed` 的 `seedColor` 换为暖色 `primary` 并显式覆盖 `colorScheme.error`。
|
||
|
||
### 6.2 新增可复用组件(建议放 `lib/widgets/` 或 `lib/features/auth/widgets/`)
|
||
|
||
| 组件 | 职责 |
|
||
| --- | --- |
|
||
| `BrandMark` | logo + 字标 + slogan,`size` 参数;Splash 与登录页共用,保证两处渲染一致(这是 §5.2 淡入过渡成立的前提) |
|
||
| `AppTextField` | 封装 `TextFormField`:label、前缀图标、errorText、enabled,密码模式内置可见性切换(含 ≥44 点击区)与 obscure 状态 |
|
||
| `PrimaryButton` | `FilledButton` + `isLoading`:loading 时换转圈、锁点击、尺寸不变。全 app 提交类按钮通用 |
|
||
| `InlineErrorBanner` | §4.2 表单级错误横幅;后续所有网络页面的错误态可复用 |
|
||
| `AuthScaffold` | 认证页统一框架:SafeArea + 滚动 + padding 24 + 键盘避让,登录/注册/找回密码共用 |
|
||
|
||
### 6.3 结构与状态(配合开发计划 §4.2)
|
||
|
||
- 新建 `lib/features/auth/`:`login_page.dart`、`register_page.dart`、`splash_page.dart`、`auth_state.dart`(或 controller)。
|
||
- 认证状态机:`unknown → authenticating → authenticated | unauthenticated`;`app.dart` 根据状态切换 `home`(替代现在直接进 `MainShellPage`),配 300ms fade。
|
||
- token 存 `flutter_secure_storage`(需新增依赖);开发计划明令禁止把 token 写进 `SharedPreferences`(现有 `AppState` 的持久化方式不可套用到凭证)。
|
||
- 表单用 `Form` + `AutofillGroup`(激活系统密码管理器自动填充),提交成功后调 `TextInput.finishAutofillContext()` 触发保存密码提示。
|
||
|
||
### 6.4 可访问性核对清单
|
||
|
||
- 触控目标 ≥44×44:主按钮 52、密码可见性切换与文字链接需显式保证点击区。
|
||
- 承载文字的色彩组合 ≥4.5:1:`primaryStrong`/白 ≈4.5、`ink`/canvas、`error`/白 ≈5.4 均达标;`#FF6F4C` 只作装饰不载文。
|
||
- errorText 由 `InputDecoration` 原生渲染,TalkBack/VoiceOver 自动关联朗读;表单级横幅出现时用 `SemanticsService.announce` 播报。
|
||
- 键盘流:账号 `next` → 密码 `done` 提交;字体随系统缩放(不写死 `textScaleFactor`)。
|
||
|
||
---
|
||
|
||
## 7. 交付验收对照(DoD)
|
||
|
||
- [ ] 登录/注册页具备 loading、错误、重试路径(本规范 §4);Splash 具备网络失败重试(§5.3)。
|
||
- [ ] 错误同时区分字段级/表单级/瞬态三层,无裸错误码透出。
|
||
- [ ] token 仅存安全存储;退出登录清除凭证并回登录页。
|
||
- [ ] 预留区(短信验证码、第三方登录)不渲染任何占位 UI,待 §12 事项确认后按本规范扩展。
|
||
- [ ] 若采纳暖色迁移:仅改 `app_theme.dart` 色值映射,全局回归五个既有 Tab 页无布局破坏。
|