调节目标温度的专用页:左右步进按钮、数字键盘弹窗、放大滚轮 ruler、按肉类显示 2–5 档熟度速选,最后通过 Save 把 (target, meat) 返回上一页(cooking 页或 meat-presets 页)。
lib/features/thermometer/presentation/pages/preset_page.dart(563 行)temperatureUnitProvider(仅订阅显示单位)MagnifyingRuler(来自 widgets/magnifying_ruler.dart),_NumpadSheet(同文件内)lib/features/thermometer/presentation/data/meat_doneness_tables.dart(27761fc / TAPD #1003051 起替代页内旧 _donenessLevels)。donenessTableFor(meatName) 按肉名返回 List<DonenessBand>,每条 band 携 donenessKey / minF / maxF / isUsda / isRecommended;recommended 行从 bands.firstWhere(isRecommended) 反取。覆盖 12 + 2 (Veal / Hamburger CM4-only 待 Liang 与 CM4 屏对齐) 种肉表,Custom 槽与未识别名走通用 5-band 默认表回退。新增 localizedDonenessKey(AppLocalizations, String) 同文件——与 meat_localization.dart 的 firmware-mapped localizedDoneness(AppLocalizations, Doneness) 区分(后者基于 5 项 enum,无 Crispy)initialTargetTempF(默认 122°F = 50°C)+ initialMeat(默认 'Beef')/preset(PresetPage.routeName)lib/app.dart 静态 routes map顶部返回按钮 + 居中标题("Set target temp")。中段大号温度数字(52pt)夹在左右两个步进 chevron 之间,点击数字弹出数字键盘 sheet。下方一条 120pt 高的放大滚轮 (MagnifyingRuler),左右滑动调温——dc19e3c 起按显示单位原生缩放:°F 模式走 32–212 °F、1 °F 粒度;°C 模式走 0–100 °C、1 °C 粒度(之前 °C 模式仍跑 °F 内部刻度 + per-tick 转换,°C 整数标签实际对应非整 °F、滑动手感不稳)。Major / mid tick 由原生显示值判定(°C 0/10/…/100 整十做 major、5/15/… 做 mid)。存储仍以 °F 为准——MagnifyingRuler.value 入参 / onChanged 出参都是 °F,单位转换只发生在边界。didUpdateWidget 在 useCelsius 翻转时也重新派生 offset(不只是 value 变化),所以温度单位 toggle 时 ruler 不会跟手势打架。下方列表显示 1 个 "Recommended" 行(仅当肉表存在 isRecommended band 时渲染)+ 当前肉种的 doneness 档(27761fc / TAPD #1003051:从 donenessTableFor(_selectedMeat) 按肉种取——Beef / Mutton / Lamb / Ostrich / Venison / Ham / Veal 5 档、Chicken 3 档含 USDA Crispy 174–179°F、Duck / Goose / Lobster / Hamburger 2 档、Fish / Pork / Turkey 3–4 档,Custom 槽 / 未识别名 走默认 5-band 回退表 Rare → Well Done),命中当前温度的行高亮,USDA 档带绿色 USDA 角标,recommended 行通过绿色文字高亮显示,不渲染渐变背景。底部两个胶囊按钮:黄色 "Presets & Goals (Beef)"(带肉种名,跳到肉类预设页)+ 橙色 "Save"。
IconButton.onPressed → Navigator.pop(context).then((result)) 收到 null → 不更新参数IconButton.onPressed(左减 1°F、右加 1°F)_targetTempF ± 1,clamp(32, 212),触发 selectionClick 震动GestureDetector.onTap → _showNumpad_NumpadSheet(数字 0-9 + Clear + Backspace + Confirm 网格);用户输入后按 Confirm 关闭并应用GestureDetector.onTap → _showNumpad
→ showModalBottomSheet(_NumpadSheet(initial, useCelsius, onConfirm: (v) => setState(_targetTempF = v)))
→ 用户输入 → _NumpadSheet._buffer 累积
→ "Set Temperature" 按钮 → _parsedTempF() (含 °C → °F 转换 + clamp 32-212) → onConfirm(temp) → Navigator.pop
MagnifyingRuler.onChanged → setState(() => _targetTempF = v)GestureDetector.onTap → _targetTempF = (rec.minF + rec.maxF) / 2,其中 rec = donenessTableFor(_selectedMeat, family: widget.family) 中通过 indexWhere 安全获取的 isRecommended band(允许为 null)(27761fc:每种肉的推荐中点不同——Beef → 134.5°F、Chicken → 170°F、Fish → 127°F、Duck → 165°F、Pork → 158.5°F …,Custom 槽 / 未识别名走默认 5-band 回退表,midpoint 仍为 134.5°F)GestureDetector.onTap → _targetTempF = (d.minF + d.maxF) / 2(每档中点)GestureDetector.onTap → navigateInstant<Map>(MeatPresetsPage())GestureDetector.onTap
→ navigateInstant<Map>(MeatPresetsPage(), settings: RouteSettings('/meat-presets'))
→ .then((result)):
result['meatType'] → setState(_selectedMeat = ...)
result['targetTempF']? → setState(_targetTempF = ...)
_selectedMeat、_isCustom、可选 _targetTempF 更新;按钮 label 变化(含肉种名)FilledButton.onPressed → Navigator.pop(context, {targetTempF, meatType})FilledButton.onPressed
→ Navigator.pop(context, {'targetTempF': _targetTempF, 'meatType': _selectedMeat})
→ cooking_page _navigateToAdjust .then((result)) 命中 → setState 更新 + _syncTargetToSession + alarmServiceProvider.reset + _pushTargetToDevice
BleDeviceService.setProbeTarget 写入设备(C5 dispatch 崩塌后的统一入口,详见 06-烹饪)仅 watch temperatureUnitProvider(用于显示单位切换)。所有调温都是本地 setState 驱动 —— 该页是「编辑器」,所有变更暂存内存,按 Save 才生效。
无显著差异。所有震动反馈在 iOS/Android 都触发。
:122 / :143 与 _NumpadSheet._parsedTempF 的 :435。120°C 的 v4.2 内温显示上限不一致 🔴 需要确认(可能是有意——预设温度仅烹饪用途;显示范围更广)。DonenessBand.donenessKey(meat_doneness_tables.dart)是 'Rare' / 'Medium Rare' / 'Medium' / 'Medium Well' / 'Well Done' / 'Crispy' 等英文字符串,再通过 localizedDonenessKey(AppLocalizations, String) switch 翻译到 ARB key。可读性 OK 但不够 idiomatic(更典型的是直接用 enum)。27761fc 起改名为 localizedDonenessKey 以与 meat_localization.dart 的 firmware-mapped localizedDoneness(AppLocalizations, Doneness) 区分——后者绑 5 项 enum,无 Crispy。