<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Gate on Tarragon</title><link>https://tarrragon.github.io/blog/tags/gate/</link><description>Recent content in Gate on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 19 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/gate/index.xml" rel="self" type="application/rss+xml"/><item><title>Gate 分類與三問設計法</title><link>https://tarrragon.github.io/blog/ux-design/02-gate-fallback/gate-three-questions/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ux-design/02-gate-fallback/gate-three-questions/</guid><description>&lt;p>&lt;a href="https://tarrragon.github.io/blog/ux-design/knowledge-cards/gate/" data-link-title="Gate（UX）" data-link-desc="說明使用者操作流程中「必須通過才能繼續」的關卡，以及成功/失敗/不確定三條路徑的設計責任">Gate&lt;/a> 是使用者操作流程中的「必須通過才能繼續」的關卡。生物辨識認證、網路連線檢查、權限請求、版本檢查 — 這些都是 gate。Gate 設計的核心責任是確保使用者在每種結果下都有路可走，而非只設計「通過」的情境。&lt;/p>
&lt;h2 id="三問設計法">三問設計法&lt;/h2>
&lt;p>每個 gate 設計時回答三個問題：&lt;/p>
&lt;h3 id="成功時做什麼">成功時做什麼&lt;/h3>
&lt;p>Gate 通過後使用者進入下一步。這是最直覺的設計 — 認證成功進入主畫面、網路連線成功開始載入資料、權限授予後啟用功能。&lt;/p>
&lt;p>成功路徑通常是設計時最先考慮的，也是最不容易遺漏的。&lt;/p>
&lt;h3 id="失敗時做什麼">失敗時做什麼&lt;/h3>
&lt;p>Gate 未通過時使用者的&lt;a href="https://tarrragon.github.io/blog/ux-design/knowledge-cards/ux-fallback/" data-link-title="Fallback（UX）" data-link-desc="說明 gate 未通過時使用者的替代路徑，和 backend fallback（server-side 降級）的語意區別">替代路徑&lt;/a>。替代路徑可以是：降級功能（部分功能可用）、替代驗證方式（密碼代替 Face ID）、手動重試（重試按鈕）、放棄操作（返回上一頁）。&lt;/p>
&lt;p>失敗路徑是最容易遺漏的。app_tunnel 的 biometric gate 設定 &lt;code>biometricOnly: true&lt;/code>，Face ID 不可用時使用者直接被擋住，沒有密碼 fallback、沒有跳過選項、沒有返回路徑（&lt;a href="https://tarrragon.github.io/blog/ux-design/cases/biometric-only-no-fallback/" data-link-title="U.C2 biometricOnly=true 無密碼 fallback" data-link-desc="Flutter app 的生物辨識設定 biometricOnly: true 阻擋所有非生物辨識認證方式 — Face ID 不可用時使用者直接被擋住，沒有替代路徑">U.C2&lt;/a>）。修復只改一個 boolean — &lt;code>biometricOnly: false&lt;/code> — 讓系統自動提示輸入裝置密碼。但這個決策應該在企劃階段做，而非實機測試時才發現。&lt;/p>
&lt;h3 id="使用者不知道發生什麼時做什麼">使用者不知道發生什麼時做什麼&lt;/h3>
&lt;p>Gate 處理中（loading）或結果不確定（timeout）時使用者看到什麼、能做什麼。&lt;/p>
&lt;p>使用者不知道發生什麼的情境包括：認證彈窗尚未出現（系統延遲）、網路請求已發但未回應（loading）、權限對話框被系統遮擋（多個 dialog 堆疊）。&lt;/p>
&lt;p>在這個狀態下使用者需要的是：知道系統在做什麼（loading 指示）、可以取消等待（取消按鈕）、超過合理時間後有提示（timeout 訊息 + 重試選項）。&lt;/p>
&lt;h2 id="gate-的四種常見類型">Gate 的四種常見類型&lt;/h2>
&lt;h3 id="認證-gate">認證 Gate&lt;/h3>
&lt;p>使用者必須驗證身份才能使用功能。生物辨識、密碼、PIN 碼、OAuth 登入。&lt;/p>
&lt;p>認證 gate 的 fallback 設計取決於安全需求和使用場景。銀行 app 可能要求生物辨識 + PIN 碼雙重驗證，沒有更低層級的 fallback。自用工具可以接受密碼 fallback，因為使用者本身就是 owner — 可用性優先於認證強度（&lt;a href="https://tarrragon.github.io/blog/ux-design/cases/biometric-only-no-fallback/" data-link-title="U.C2 biometricOnly=true 無密碼 fallback" data-link-desc="Flutter app 的生物辨識設定 biometricOnly: true 阻擋所有非生物辨識認證方式 — Face ID 不可用時使用者直接被擋住，沒有替代路徑">U.C2&lt;/a>）。&lt;/p>
&lt;h3 id="網路-gate">網路 Gate&lt;/h3>
&lt;p>功能需要網路連線才能運作。連線存在但不穩定的場景比完全離線更難處理 — 請求可能成功、可能逾時、可能部分成功。&lt;/p>
&lt;h3 id="權限-gate">權限 Gate&lt;/h3>
&lt;p>App 需要系統權限（相機、位置、通知）才能使用特定功能。&lt;/p>
&lt;p>權限 gate 的特殊性在於使用者可以永久拒絕。拒絕後再次請求不會彈出系統對話框 — 必須引導使用者到系統設定手動開啟。&lt;/p>
&lt;h3 id="環境-gate">環境 Gate&lt;/h3>
&lt;p>特定的硬體或軟體條件必須滿足。最低 OS 版本、特定感測器（NFC、深度相機）、特定連接（藍牙已開啟）。&lt;/p>
&lt;p>環境 gate 的 fallback 通常有限 — 硬體不存在時無法用軟體模擬。但至少應該告知使用者為什麼功能不可用，而非靜默禁用。&lt;/p>
&lt;h3 id="其他常見-gate">其他常見 Gate&lt;/h3>
&lt;p>商業 app 還有兩種 gate 在本系列涵蓋範圍之外但實務常見：&lt;/p>
&lt;p>&lt;strong>付費 Gate&lt;/strong>（paywall）：功能需要付費才能使用。付費 gate 的 fallback 設計和上述四種不同 — 「失敗」路徑的目標是引導使用者付費而非提供替代功能。試用期、降級功能、付費引導 vs 付費強制的取捨依賴商業模式決策。&lt;/p>
&lt;p>&lt;strong>版本相容性 Gate&lt;/strong>：API 版本過舊需要升級 app。Fallback 是提示使用者更新，但強制更新會阻擋無法更新的使用者（舊 OS 版本不支援新版 app）。&lt;/p>
&lt;h2 id="gate-設計表">Gate 設計表&lt;/h2>
&lt;p>把三問設計法應用到每個 gate，產出一張設計表：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Gate&lt;/th>
 &lt;th>成功&lt;/th>
 &lt;th>失敗&lt;/th>
 &lt;th>不確定&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>生物辨識&lt;/td>
 &lt;td>進入主畫面&lt;/td>
 &lt;td>提示輸入裝置密碼&lt;/td>
 &lt;td>顯示「驗證中」&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>網路連線&lt;/td>
 &lt;td>開始載入資料&lt;/td>
 &lt;td>顯示離線提示 + 重試&lt;/td>
 &lt;td>顯示 loading + 取消&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>相機權限&lt;/td>
 &lt;td>開啟掃描功能&lt;/td>
 &lt;td>說明原因 + 設定連結&lt;/td>
 &lt;td>等待系統對話框&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>藍牙&lt;/td>
 &lt;td>開始裝置搜尋&lt;/td>
 &lt;td>提示開啟藍牙 + 連結&lt;/td>
 &lt;td>顯示搜尋中 + 取消&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>失敗欄和不確定欄為空的 gate 就是 UX 死胡同的候選 — 和&lt;a href="https://tarrragon.github.io/blog/ux-design/01-screen-state-machine/state-matrix-definition/" data-link-title="畫面狀態矩陣的定義與填寫方法" data-link-desc="四欄矩陣（顯示 / 可用操作 / 進入條件 / 退出路徑）的定義、填寫步驟和檢查規則 — 退出路徑為空 = UX 死胡同">畫面狀態矩陣&lt;/a>的退出路徑檢查同樣的邏輯。&lt;/p></description><content:encoded><![CDATA[<p><a href="/blog/ux-design/knowledge-cards/gate/" data-link-title="Gate（UX）" data-link-desc="說明使用者操作流程中「必須通過才能繼續」的關卡，以及成功/失敗/不確定三條路徑的設計責任">Gate</a> 是使用者操作流程中的「必須通過才能繼續」的關卡。生物辨識認證、網路連線檢查、權限請求、版本檢查 — 這些都是 gate。Gate 設計的核心責任是確保使用者在每種結果下都有路可走，而非只設計「通過」的情境。</p>
<h2 id="三問設計法">三問設計法</h2>
<p>每個 gate 設計時回答三個問題：</p>
<h3 id="成功時做什麼">成功時做什麼</h3>
<p>Gate 通過後使用者進入下一步。這是最直覺的設計 — 認證成功進入主畫面、網路連線成功開始載入資料、權限授予後啟用功能。</p>
<p>成功路徑通常是設計時最先考慮的，也是最不容易遺漏的。</p>
<h3 id="失敗時做什麼">失敗時做什麼</h3>
<p>Gate 未通過時使用者的<a href="/blog/ux-design/knowledge-cards/ux-fallback/" data-link-title="Fallback（UX）" data-link-desc="說明 gate 未通過時使用者的替代路徑，和 backend fallback（server-side 降級）的語意區別">替代路徑</a>。替代路徑可以是：降級功能（部分功能可用）、替代驗證方式（密碼代替 Face ID）、手動重試（重試按鈕）、放棄操作（返回上一頁）。</p>
<p>失敗路徑是最容易遺漏的。app_tunnel 的 biometric gate 設定 <code>biometricOnly: true</code>，Face ID 不可用時使用者直接被擋住，沒有密碼 fallback、沒有跳過選項、沒有返回路徑（<a href="/blog/ux-design/cases/biometric-only-no-fallback/" data-link-title="U.C2 biometricOnly=true 無密碼 fallback" data-link-desc="Flutter app 的生物辨識設定 biometricOnly: true 阻擋所有非生物辨識認證方式 — Face ID 不可用時使用者直接被擋住，沒有替代路徑">U.C2</a>）。修復只改一個 boolean — <code>biometricOnly: false</code> — 讓系統自動提示輸入裝置密碼。但這個決策應該在企劃階段做，而非實機測試時才發現。</p>
<h3 id="使用者不知道發生什麼時做什麼">使用者不知道發生什麼時做什麼</h3>
<p>Gate 處理中（loading）或結果不確定（timeout）時使用者看到什麼、能做什麼。</p>
<p>使用者不知道發生什麼的情境包括：認證彈窗尚未出現（系統延遲）、網路請求已發但未回應（loading）、權限對話框被系統遮擋（多個 dialog 堆疊）。</p>
<p>在這個狀態下使用者需要的是：知道系統在做什麼（loading 指示）、可以取消等待（取消按鈕）、超過合理時間後有提示（timeout 訊息 + 重試選項）。</p>
<h2 id="gate-的四種常見類型">Gate 的四種常見類型</h2>
<h3 id="認證-gate">認證 Gate</h3>
<p>使用者必須驗證身份才能使用功能。生物辨識、密碼、PIN 碼、OAuth 登入。</p>
<p>認證 gate 的 fallback 設計取決於安全需求和使用場景。銀行 app 可能要求生物辨識 + PIN 碼雙重驗證，沒有更低層級的 fallback。自用工具可以接受密碼 fallback，因為使用者本身就是 owner — 可用性優先於認證強度（<a href="/blog/ux-design/cases/biometric-only-no-fallback/" data-link-title="U.C2 biometricOnly=true 無密碼 fallback" data-link-desc="Flutter app 的生物辨識設定 biometricOnly: true 阻擋所有非生物辨識認證方式 — Face ID 不可用時使用者直接被擋住，沒有替代路徑">U.C2</a>）。</p>
<h3 id="網路-gate">網路 Gate</h3>
<p>功能需要網路連線才能運作。連線存在但不穩定的場景比完全離線更難處理 — 請求可能成功、可能逾時、可能部分成功。</p>
<h3 id="權限-gate">權限 Gate</h3>
<p>App 需要系統權限（相機、位置、通知）才能使用特定功能。</p>
<p>權限 gate 的特殊性在於使用者可以永久拒絕。拒絕後再次請求不會彈出系統對話框 — 必須引導使用者到系統設定手動開啟。</p>
<h3 id="環境-gate">環境 Gate</h3>
<p>特定的硬體或軟體條件必須滿足。最低 OS 版本、特定感測器（NFC、深度相機）、特定連接（藍牙已開啟）。</p>
<p>環境 gate 的 fallback 通常有限 — 硬體不存在時無法用軟體模擬。但至少應該告知使用者為什麼功能不可用，而非靜默禁用。</p>
<h3 id="其他常見-gate">其他常見 Gate</h3>
<p>商業 app 還有兩種 gate 在本系列涵蓋範圍之外但實務常見：</p>
<p><strong>付費 Gate</strong>（paywall）：功能需要付費才能使用。付費 gate 的 fallback 設計和上述四種不同 — 「失敗」路徑的目標是引導使用者付費而非提供替代功能。試用期、降級功能、付費引導 vs 付費強制的取捨依賴商業模式決策。</p>
<p><strong>版本相容性 Gate</strong>：API 版本過舊需要升級 app。Fallback 是提示使用者更新，但強制更新會阻擋無法更新的使用者（舊 OS 版本不支援新版 app）。</p>
<h2 id="gate-設計表">Gate 設計表</h2>
<p>把三問設計法應用到每個 gate，產出一張設計表：</p>
<table>
  <thead>
      <tr>
          <th>Gate</th>
          <th>成功</th>
          <th>失敗</th>
          <th>不確定</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>生物辨識</td>
          <td>進入主畫面</td>
          <td>提示輸入裝置密碼</td>
          <td>顯示「驗證中」</td>
      </tr>
      <tr>
          <td>網路連線</td>
          <td>開始載入資料</td>
          <td>顯示離線提示 + 重試</td>
          <td>顯示 loading + 取消</td>
      </tr>
      <tr>
          <td>相機權限</td>
          <td>開啟掃描功能</td>
          <td>說明原因 + 設定連結</td>
          <td>等待系統對話框</td>
      </tr>
      <tr>
          <td>藍牙</td>
          <td>開始裝置搜尋</td>
          <td>提示開啟藍牙 + 連結</td>
          <td>顯示搜尋中 + 取消</td>
      </tr>
  </tbody>
</table>
<p>失敗欄和不確定欄為空的 gate 就是 UX 死胡同的候選 — 和<a href="/blog/ux-design/01-screen-state-machine/state-matrix-definition/" data-link-title="畫面狀態矩陣的定義與填寫方法" data-link-desc="四欄矩陣（顯示 / 可用操作 / 進入條件 / 退出路徑）的定義、填寫步驟和檢查規則 — 退出路徑為空 = UX 死胡同">畫面狀態矩陣</a>的退出路徑檢查同樣的邏輯。</p>
<p>三問設計法的具體應用在 <a href="/blog/ux-design/02-gate-fallback/biometric-fallback-design/" data-link-title="Biometric fallback 完整設計" data-link-desc="iOS Face ID / Touch ID 和 Android BiometricPrompt 的行為差異、fallback 策略、安全 vs 可用性取捨的顯式記錄方法">Biometric fallback 完整設計</a>中以生物辨識 gate 為例展開。Gate 在開發環境的行為可能和真機不同，<a href="/blog/ux-design/02-gate-fallback/dev-vs-real-gate-behavior/" data-link-title="開發環境 vs 真機的 gate 行為差異表" data-link-desc="模擬器、debug build、test 環境中的 gate 行為和真機 release build 不同 — 差異表讓開發者在上機前知道哪些 gate 還沒被真實驗證">開發環境 vs 真機的 gate 行為差異表</a>列出每個 gate 在模擬器和真機上的差異。Gate 設計表的「失敗」欄和<a href="/blog/ux-design/01-screen-state-machine/" data-link-title="模組一：畫面狀態機設計" data-link-desc="畫面狀態矩陣（顯示 / 操作 / 進入 / 退出）— 退出路徑為空 = UX 死胡同">畫面狀態矩陣</a>的「退出路徑」欄是同一個問題在不同層級的表達。</p>
]]></content:encoded></item><item><title>Gate（UX）</title><link>https://tarrragon.github.io/blog/ux-design/knowledge-cards/gate/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ux-design/knowledge-cards/gate/</guid><description>&lt;p>Gate 的核心概念是「使用者操作流程中必須通過才能繼續的關卡」。認證、網路連線、權限請求、環境檢查、付費牆都是 gate。每個 gate 需要設計三條路徑：成功時做什麼、失敗時做什麼、使用者不知道發生什麼時做什麼。可先對照 &lt;a href="https://tarrragon.github.io/blog/ux-design/knowledge-cards/ux-fallback/" data-link-title="Fallback（UX）" data-link-desc="說明 gate 未通過時使用者的替代路徑，和 backend fallback（server-side 降級）的語意區別">Fallback（UX）&lt;/a> 和 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/fallback/" data-link-title="Fallback" data-link-desc="說明主要路徑失敗時使用替代結果或替代流程的設計責任">Fallback（Backend）&lt;/a>。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>UX 語境的 gate 聚焦在使用者體驗層 — 關注的是「使用者被擋住時看到什麼、能做什麼」。和 backend 語境的 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/gate-decision/" data-link-title="Gate Decision" data-link-desc="說明 release gate 如何把證據轉成放行、暫停、回退或補證據的決策">gate decision&lt;/a> 不同，後者關注的是部署流程中的品質關卡。Gate 的失敗路徑和不確定路徑應該反映在&lt;a href="https://tarrragon.github.io/blog/ux-design/knowledge-cards/screen-state-matrix/" data-link-title="畫面狀態矩陣" data-link-desc="說明用四欄表格（顯示/可用操作/進入條件/退出路徑）系統性地暴露畫面導航缺口的設計工具">畫面狀態矩陣&lt;/a>的退出路徑欄中。&lt;/p>
&lt;h2 id="可觀察訊號與例子">可觀察訊號與例子&lt;/h2>
&lt;p>需要 gate 設計的訊號是使用者在某個功能前被阻擋且沒有替代路徑。常見情境：biometric 認證失敗後使用者無法進入 app、網路斷線後使用者被困在 loading 畫面、權限被拒後功能靜默消失但使用者不知道為什麼。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Gate 的設計責任是確保每條路徑都有明確的使用者體驗。成功路徑通常最先被設計；失敗路徑需要提供 &lt;a href="https://tarrragon.github.io/blog/ux-design/knowledge-cards/ux-fallback/" data-link-title="Fallback（UX）" data-link-desc="說明 gate 未通過時使用者的替代路徑，和 backend fallback（server-side 降級）的語意區別">UX fallback&lt;/a>（替代驗證、降級功能、返回上一頁）；不確定路徑需要 loading 指示和取消操作。開發環境可能遮蔽 gate 問題 — 模擬器跳過認證、debug build 自動授權 — 差異表讓開發者在上機前知道哪些 gate 還沒被真實驗證。&lt;/p></description><content:encoded><![CDATA[<p>Gate 的核心概念是「使用者操作流程中必須通過才能繼續的關卡」。認證、網路連線、權限請求、環境檢查、付費牆都是 gate。每個 gate 需要設計三條路徑：成功時做什麼、失敗時做什麼、使用者不知道發生什麼時做什麼。可先對照 <a href="/blog/ux-design/knowledge-cards/ux-fallback/" data-link-title="Fallback（UX）" data-link-desc="說明 gate 未通過時使用者的替代路徑，和 backend fallback（server-side 降級）的語意區別">Fallback（UX）</a> 和 <a href="/blog/backend/knowledge-cards/fallback/" data-link-title="Fallback" data-link-desc="說明主要路徑失敗時使用替代結果或替代流程的設計責任">Fallback（Backend）</a>。</p>
<h2 id="概念位置">概念位置</h2>
<p>UX 語境的 gate 聚焦在使用者體驗層 — 關注的是「使用者被擋住時看到什麼、能做什麼」。和 backend 語境的 <a href="/blog/backend/knowledge-cards/gate-decision/" data-link-title="Gate Decision" data-link-desc="說明 release gate 如何把證據轉成放行、暫停、回退或補證據的決策">gate decision</a> 不同，後者關注的是部署流程中的品質關卡。Gate 的失敗路徑和不確定路徑應該反映在<a href="/blog/ux-design/knowledge-cards/screen-state-matrix/" data-link-title="畫面狀態矩陣" data-link-desc="說明用四欄表格（顯示/可用操作/進入條件/退出路徑）系統性地暴露畫面導航缺口的設計工具">畫面狀態矩陣</a>的退出路徑欄中。</p>
<h2 id="可觀察訊號與例子">可觀察訊號與例子</h2>
<p>需要 gate 設計的訊號是使用者在某個功能前被阻擋且沒有替代路徑。常見情境：biometric 認證失敗後使用者無法進入 app、網路斷線後使用者被困在 loading 畫面、權限被拒後功能靜默消失但使用者不知道為什麼。</p>
<h2 id="設計責任">設計責任</h2>
<p>Gate 的設計責任是確保每條路徑都有明確的使用者體驗。成功路徑通常最先被設計；失敗路徑需要提供 <a href="/blog/ux-design/knowledge-cards/ux-fallback/" data-link-title="Fallback（UX）" data-link-desc="說明 gate 未通過時使用者的替代路徑，和 backend fallback（server-side 降級）的語意區別">UX fallback</a>（替代驗證、降級功能、返回上一頁）；不確定路徑需要 loading 指示和取消操作。開發環境可能遮蔽 gate 問題 — 模擬器跳過認證、debug build 自動授權 — 差異表讓開發者在上機前知道哪些 gate 還沒被真實驗證。</p>
]]></content:encoded></item><item><title>模組二：Gate 與 Fallback 設計</title><link>https://tarrragon.github.io/blog/ux-design/02-gate-fallback/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ux-design/02-gate-fallback/</guid><description>&lt;p>回答「使用者過不了關卡時怎麼辦」。&lt;/p>
&lt;h2 id="對應-findings">對應 findings&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Finding&lt;/th>
 &lt;th>來源&lt;/th>
 &lt;th>內容&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>UF-4&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ux-design/cases/biometric-only-no-fallback/" data-link-title="U.C2 biometricOnly=true 無密碼 fallback" data-link-desc="Flutter app 的生物辨識設定 biometricOnly: true 阻擋所有非生物辨識認證方式 — Face ID 不可用時使用者直接被擋住，沒有替代路徑">U.C2&lt;/a>&lt;/td>
 &lt;td>biometricOnly 安全收益 vs 可用性代價 — &lt;strong>本模組主寫&lt;/strong>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>UF-5&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ux-design/cases/biometric-only-no-fallback/" data-link-title="U.C2 biometricOnly=true 無密碼 fallback" data-link-desc="Flutter app 的生物辨識設定 biometricOnly: true 阻擋所有非生物辨識認證方式 — Face ID 不可用時使用者直接被擋住，沒有替代路徑">U.C2&lt;/a>&lt;/td>
 &lt;td>開發環境遮蔽 gate 問題（模擬器行為 vs 真機）&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="待寫章節">待寫章節&lt;/h2>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Gate 分類與三問設計法（成功 / 失敗 / 使用者不知道發生什麼）&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Biometric fallback 完整設計（iOS/Android 差異）&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> 網路斷線 UX 模式（offline-first / retry / degraded mode）&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Permission 請求時機與措辭&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> 開發環境 vs 真機的 gate 行為差異表&lt;/li>
&lt;/ul>
&lt;h2 id="跨分類引用">跨分類引用&lt;/h2>
&lt;ul>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/testing/01-test-strategy-layers/" data-link-title="模組一：測試策略分層" data-link-desc="Unit / Protocol Integration / Screen State 三層測試各自的職責、盲區和判斷原則">testing 模組一 測試策略&lt;/a>：gate fallback 的 mock vs 真機行為差異需要 protocol test&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/monitoring/07-security-privacy/" data-link-title="模組七：資安與隱私" data-link-desc="SDK redaction / transport 加密 / collector access control / 去識別化 — 蒐集的資料本身就是風險資產">monitoring 模組七 資安&lt;/a>：biometric fallback 的安全 vs 可用性取捨&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>回答「使用者過不了關卡時怎麼辦」。</p>
<h2 id="對應-findings">對應 findings</h2>
<table>
  <thead>
      <tr>
          <th>Finding</th>
          <th>來源</th>
          <th>內容</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>UF-4</td>
          <td><a href="/blog/ux-design/cases/biometric-only-no-fallback/" data-link-title="U.C2 biometricOnly=true 無密碼 fallback" data-link-desc="Flutter app 的生物辨識設定 biometricOnly: true 阻擋所有非生物辨識認證方式 — Face ID 不可用時使用者直接被擋住，沒有替代路徑">U.C2</a></td>
          <td>biometricOnly 安全收益 vs 可用性代價 — <strong>本模組主寫</strong></td>
      </tr>
      <tr>
          <td>UF-5</td>
          <td><a href="/blog/ux-design/cases/biometric-only-no-fallback/" data-link-title="U.C2 biometricOnly=true 無密碼 fallback" data-link-desc="Flutter app 的生物辨識設定 biometricOnly: true 阻擋所有非生物辨識認證方式 — Face ID 不可用時使用者直接被擋住，沒有替代路徑">U.C2</a></td>
          <td>開發環境遮蔽 gate 問題（模擬器行為 vs 真機）</td>
      </tr>
  </tbody>
</table>
<h2 id="待寫章節">待寫章節</h2>
<ul>
<li><input checked="" disabled="" type="checkbox"> Gate 分類與三問設計法（成功 / 失敗 / 使用者不知道發生什麼）</li>
<li><input checked="" disabled="" type="checkbox"> Biometric fallback 完整設計（iOS/Android 差異）</li>
<li><input checked="" disabled="" type="checkbox"> 網路斷線 UX 模式（offline-first / retry / degraded mode）</li>
<li><input checked="" disabled="" type="checkbox"> Permission 請求時機與措辭</li>
<li><input checked="" disabled="" type="checkbox"> 開發環境 vs 真機的 gate 行為差異表</li>
</ul>
<h2 id="跨分類引用">跨分類引用</h2>
<ul>
<li>→ <a href="/blog/testing/01-test-strategy-layers/" data-link-title="模組一：測試策略分層" data-link-desc="Unit / Protocol Integration / Screen State 三層測試各自的職責、盲區和判斷原則">testing 模組一 測試策略</a>：gate fallback 的 mock vs 真機行為差異需要 protocol test</li>
<li>→ <a href="/blog/monitoring/07-security-privacy/" data-link-title="模組七：資安與隱私" data-link-desc="SDK redaction / transport 加密 / collector access control / 去識別化 — 蒐集的資料本身就是風險資產">monitoring 模組七 資安</a>：biometric fallback 的安全 vs 可用性取捨</li>
</ul>
]]></content:encoded></item><item><title>Fallback（UX）</title><link>https://tarrragon.github.io/blog/ux-design/knowledge-cards/ux-fallback/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ux-design/knowledge-cards/ux-fallback/</guid><description>&lt;p>UX fallback 的核心概念是「gate 未通過時使用者的替代路徑」。替代路徑可以是替代驗證方式（密碼代替 Face ID）、降級功能（部分功能可用）、手動重試、或放棄操作返回上一頁。和 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/fallback/" data-link-title="Fallback" data-link-desc="說明主要路徑失敗時使用替代結果或替代流程的設計責任">Fallback（Backend）&lt;/a> 不同，UX fallback 關注的是使用者體驗層的路徑設計，而非 server-side 的服務降級策略。可先對照 &lt;a href="https://tarrragon.github.io/blog/ux-design/knowledge-cards/gate/" data-link-title="Gate（UX）" data-link-desc="說明使用者操作流程中「必須通過才能繼續」的關卡，以及成功/失敗/不確定三條路徑的設計責任">Gate&lt;/a>。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>UX fallback 位在 &lt;a href="https://tarrragon.github.io/blog/ux-design/knowledge-cards/gate/" data-link-title="Gate（UX）" data-link-desc="說明使用者操作流程中「必須通過才能繼續」的關卡，以及成功/失敗/不確定三條路徑的設計責任">Gate&lt;/a> 設計的失敗路徑中。Gate 的三問（成功/失敗/不確定）中，失敗路徑的具體內容就是 UX fallback。Backend 的 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/fallback/" data-link-title="Fallback" data-link-desc="說明主要路徑失敗時使用替代結果或替代流程的設計責任">fallback&lt;/a> 是系統在依賴失敗時用替代結果維持服務，UX fallback 是使用者在 gate 失敗時的操作替代方案。兩者可能並存 — server-side fallback 提供降級資料，UX fallback 決定如何呈現這些降級資料給使用者。&lt;/p>
&lt;h2 id="可觀察訊號與例子">可觀察訊號與例子&lt;/h2>
&lt;p>需要 UX fallback 的訊號是 gate 失敗時使用者完全無法繼續。常見情境：biometric 設定 &lt;code>biometricOnly: true&lt;/code> 導致 Face ID 失敗時沒有密碼 fallback、error 畫面只有重試按鈕沒有返回按鈕、網路斷線後所有功能不可用但部分功能不依賴網路。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>UX fallback 的設計責任是確保 gate 失敗時使用者有路可走。Fallback 的選擇取決於安全需求和使用場景 — 銀行 app 可能不提供低安全等級的 fallback，自用工具可以接受密碼 fallback 因為使用者就是 owner。安全 vs 可用性的取捨應在功能規格中顯式記錄。UX fallback 的存在應反映在&lt;a href="https://tarrragon.github.io/blog/ux-design/knowledge-cards/screen-state-matrix/" data-link-title="畫面狀態矩陣" data-link-desc="說明用四欄表格（顯示/可用操作/進入條件/退出路徑）系統性地暴露畫面導航缺口的設計工具">畫面狀態矩陣&lt;/a>的退出路徑欄中。&lt;/p></description><content:encoded><![CDATA[<p>UX fallback 的核心概念是「gate 未通過時使用者的替代路徑」。替代路徑可以是替代驗證方式（密碼代替 Face ID）、降級功能（部分功能可用）、手動重試、或放棄操作返回上一頁。和 <a href="/blog/backend/knowledge-cards/fallback/" data-link-title="Fallback" data-link-desc="說明主要路徑失敗時使用替代結果或替代流程的設計責任">Fallback（Backend）</a> 不同，UX fallback 關注的是使用者體驗層的路徑設計，而非 server-side 的服務降級策略。可先對照 <a href="/blog/ux-design/knowledge-cards/gate/" data-link-title="Gate（UX）" data-link-desc="說明使用者操作流程中「必須通過才能繼續」的關卡，以及成功/失敗/不確定三條路徑的設計責任">Gate</a>。</p>
<h2 id="概念位置">概念位置</h2>
<p>UX fallback 位在 <a href="/blog/ux-design/knowledge-cards/gate/" data-link-title="Gate（UX）" data-link-desc="說明使用者操作流程中「必須通過才能繼續」的關卡，以及成功/失敗/不確定三條路徑的設計責任">Gate</a> 設計的失敗路徑中。Gate 的三問（成功/失敗/不確定）中，失敗路徑的具體內容就是 UX fallback。Backend 的 <a href="/blog/backend/knowledge-cards/fallback/" data-link-title="Fallback" data-link-desc="說明主要路徑失敗時使用替代結果或替代流程的設計責任">fallback</a> 是系統在依賴失敗時用替代結果維持服務，UX fallback 是使用者在 gate 失敗時的操作替代方案。兩者可能並存 — server-side fallback 提供降級資料，UX fallback 決定如何呈現這些降級資料給使用者。</p>
<h2 id="可觀察訊號與例子">可觀察訊號與例子</h2>
<p>需要 UX fallback 的訊號是 gate 失敗時使用者完全無法繼續。常見情境：biometric 設定 <code>biometricOnly: true</code> 導致 Face ID 失敗時沒有密碼 fallback、error 畫面只有重試按鈕沒有返回按鈕、網路斷線後所有功能不可用但部分功能不依賴網路。</p>
<h2 id="設計責任">設計責任</h2>
<p>UX fallback 的設計責任是確保 gate 失敗時使用者有路可走。Fallback 的選擇取決於安全需求和使用場景 — 銀行 app 可能不提供低安全等級的 fallback，自用工具可以接受密碼 fallback 因為使用者就是 owner。安全 vs 可用性的取捨應在功能規格中顯式記錄。UX fallback 的存在應反映在<a href="/blog/ux-design/knowledge-cards/screen-state-matrix/" data-link-title="畫面狀態矩陣" data-link-desc="說明用四欄表格（顯示/可用操作/進入條件/退出路徑）系統性地暴露畫面導航缺口的設計工具">畫面狀態矩陣</a>的退出路徑欄中。</p>
]]></content:encoded></item><item><title>網路斷線 UX 模式</title><link>https://tarrragon.github.io/blog/ux-design/02-gate-fallback/network-offline-ux/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ux-design/02-gate-fallback/network-offline-ux/</guid><description>&lt;p>網路 &lt;a href="https://tarrragon.github.io/blog/ux-design/knowledge-cards/gate/" data-link-title="Gate（UX）" data-link-desc="說明使用者操作流程中「必須通過才能繼續」的關卡，以及成功/失敗/不確定三條路徑的設計責任">gate&lt;/a> 和其他 gate 的差異在於狀態的連續性。生物辨識是二元結果（通過或不通過），網路狀態是連續的 — 連線中、已連線、斷線、重新連線、連線但延遲高、連線但頻繁斷開。處理策略取決於功能對即時連線的依賴程度。&lt;/p>
&lt;h2 id="三種處理策略">三種處理策略&lt;/h2>
&lt;h3 id="offline-first">Offline-first&lt;/h3>
&lt;p>功能的核心操作在本地完成，網路用於同步。斷線時使用者仍可操作，重新連線後自動同步差異。&lt;/p>
&lt;p>Offline-first 適合的前提是資料可以本地存儲且衝突可以解決。筆記 app、待辦事項、表單填寫 — 使用者的操作產生本地資料，網路只負責把資料同步到 server。&lt;/p>
&lt;p>Offline-first 的 UX 設計重點是讓使用者知道同步狀態：已同步、待同步、同步失敗。不需要 gate — 網路狀態不阻擋使用者操作。&lt;/p>
&lt;h3 id="retry-with-feedback">Retry with feedback&lt;/h3>
&lt;p>功能需要網路但可以等待。斷線時顯示狀態和重試選項，使用者決定要等還是離開。&lt;/p>
&lt;p>app_tunnel 的 terminal 連線屬於這個模式。WebSocket 連線需要網路，斷線時使用者無法操作終端機。error 和 disconnected 狀態提供重連按鈕讓使用者手動重試。&lt;/p>
&lt;p>Retry 策略的 UX 設計重點：&lt;/p>
&lt;ul>
&lt;li>告知使用者發生什麼（「連線中斷」而非空白畫面）&lt;/li>
&lt;li>提供手動重試（重連按鈕）&lt;/li>
&lt;li>提供退出路徑（返回首頁 — app_tunnel 原本缺少這個）&lt;/li>
&lt;li>自動重試要有上限和間隔遞增（避免無限重試消耗電量）&lt;/li>
&lt;/ul>
&lt;h3 id="degraded-mode">Degraded mode&lt;/h3>
&lt;p>功能部分依賴網路。核心功能離線可用，進階功能需要網路。斷線時自動切換到降級模式，不阻擋使用者操作但功能受限。&lt;/p>
&lt;p>降級模式的 UX 設計重點是清楚標示哪些功能可用、哪些不可用。「離線模式 — 搜尋功能暫時不可用」比靜默隱藏搜尋按鈕更透明。&lt;/p>
&lt;h2 id="網路狀態的-ui-呈現">網路狀態的 UI 呈現&lt;/h2>
&lt;h3 id="全域指示器">全域指示器&lt;/h3>
&lt;p>在 app 頂部或狀態列顯示「離線」標示。適合網路狀態影響全域功能的 app。&lt;/p>
&lt;h3 id="功能級指示器">功能級指示器&lt;/h3>
&lt;p>在需要網路的功能旁邊顯示不可用狀態。適合只有部分功能依賴網路的 app。&lt;/p>
&lt;h3 id="非侵入式通知">非侵入式通知&lt;/h3>
&lt;p>用 Snackbar 或 Toast 短暫顯示「已恢復連線」或「網路中斷」。適合網路狀態偶爾變化的場景。不適合頻繁斷開重連的場景（通知太多會干擾使用者）。&lt;/p>
&lt;h2 id="連線但品質差的場景">連線但品質差的場景&lt;/h2>
&lt;p>網路存在但延遲高或頻繁斷開，比完全離線更難處理。完全離線時 app 可以立即切換到離線模式；連線不穩定時，每次請求可能成功也可能逾時，使用者體驗是「有時候行有時候不行」。&lt;/p>
&lt;p>處理策略：&lt;/p>
&lt;ul>
&lt;li>設定合理的逾時時間（太短會把慢回應判定為失敗，太長讓使用者等太久）&lt;/li>
&lt;li>逾時後顯示狀態和重試選項，不自動重試（避免在不穩定網路上累積重試）&lt;/li>
&lt;li>在 loading 狀態提供取消選項，讓使用者可以中斷等待&lt;/li>
&lt;/ul>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>Gate 設計的通用方法論 → &lt;a href="https://tarrragon.github.io/blog/ux-design/02-gate-fallback/gate-three-questions/" data-link-title="Gate 分類與三問設計法" data-link-desc="每個 gate 設計時問三個問題：成功時做什麼、失敗時做什麼、使用者不知道發生什麼時做什麼">Gate 分類與三問設計法&lt;/a>&lt;/li>
&lt;li>權限請求的 UX 設計 → &lt;a href="https://tarrragon.github.io/blog/ux-design/02-gate-fallback/permission-request-timing/" data-link-title="Permission 請求時機與措辭" data-link-desc="系統權限請求的時機選擇（首次開啟 vs 功能使用時）和說明文字的設計 — 使用者只有一次機會理解為什麼需要這個權限">Permission 請求時機與措辭&lt;/a>&lt;/li>
&lt;li>畫面狀態矩陣中的網路狀態 → &lt;a href="https://tarrragon.github.io/blog/ux-design/01-screen-state-machine/" data-link-title="模組一：畫面狀態機設計" data-link-desc="畫面狀態矩陣（顯示 / 操作 / 進入 / 退出）— 退出路徑為空 = UX 死胡同">ux-design 模組一 畫面狀態機&lt;/a>&lt;/li>
&lt;li>Server 端背壓如何影響 client UX → &lt;a href="https://tarrragon.github.io/blog/devops/03-traffic-management/backpressure/" data-link-title="背壓機制" data-link-desc="下游處理慢時上游怎麼減速 — 有限 buffer &amp;#43; 回壓訊號的設計、和 rate limit 的區別">DevOps 背壓機制&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>網路 <a href="/blog/ux-design/knowledge-cards/gate/" data-link-title="Gate（UX）" data-link-desc="說明使用者操作流程中「必須通過才能繼續」的關卡，以及成功/失敗/不確定三條路徑的設計責任">gate</a> 和其他 gate 的差異在於狀態的連續性。生物辨識是二元結果（通過或不通過），網路狀態是連續的 — 連線中、已連線、斷線、重新連線、連線但延遲高、連線但頻繁斷開。處理策略取決於功能對即時連線的依賴程度。</p>
<h2 id="三種處理策略">三種處理策略</h2>
<h3 id="offline-first">Offline-first</h3>
<p>功能的核心操作在本地完成，網路用於同步。斷線時使用者仍可操作，重新連線後自動同步差異。</p>
<p>Offline-first 適合的前提是資料可以本地存儲且衝突可以解決。筆記 app、待辦事項、表單填寫 — 使用者的操作產生本地資料，網路只負責把資料同步到 server。</p>
<p>Offline-first 的 UX 設計重點是讓使用者知道同步狀態：已同步、待同步、同步失敗。不需要 gate — 網路狀態不阻擋使用者操作。</p>
<h3 id="retry-with-feedback">Retry with feedback</h3>
<p>功能需要網路但可以等待。斷線時顯示狀態和重試選項，使用者決定要等還是離開。</p>
<p>app_tunnel 的 terminal 連線屬於這個模式。WebSocket 連線需要網路，斷線時使用者無法操作終端機。error 和 disconnected 狀態提供重連按鈕讓使用者手動重試。</p>
<p>Retry 策略的 UX 設計重點：</p>
<ul>
<li>告知使用者發生什麼（「連線中斷」而非空白畫面）</li>
<li>提供手動重試（重連按鈕）</li>
<li>提供退出路徑（返回首頁 — app_tunnel 原本缺少這個）</li>
<li>自動重試要有上限和間隔遞增（避免無限重試消耗電量）</li>
</ul>
<h3 id="degraded-mode">Degraded mode</h3>
<p>功能部分依賴網路。核心功能離線可用，進階功能需要網路。斷線時自動切換到降級模式，不阻擋使用者操作但功能受限。</p>
<p>降級模式的 UX 設計重點是清楚標示哪些功能可用、哪些不可用。「離線模式 — 搜尋功能暫時不可用」比靜默隱藏搜尋按鈕更透明。</p>
<h2 id="網路狀態的-ui-呈現">網路狀態的 UI 呈現</h2>
<h3 id="全域指示器">全域指示器</h3>
<p>在 app 頂部或狀態列顯示「離線」標示。適合網路狀態影響全域功能的 app。</p>
<h3 id="功能級指示器">功能級指示器</h3>
<p>在需要網路的功能旁邊顯示不可用狀態。適合只有部分功能依賴網路的 app。</p>
<h3 id="非侵入式通知">非侵入式通知</h3>
<p>用 Snackbar 或 Toast 短暫顯示「已恢復連線」或「網路中斷」。適合網路狀態偶爾變化的場景。不適合頻繁斷開重連的場景（通知太多會干擾使用者）。</p>
<h2 id="連線但品質差的場景">連線但品質差的場景</h2>
<p>網路存在但延遲高或頻繁斷開，比完全離線更難處理。完全離線時 app 可以立即切換到離線模式；連線不穩定時，每次請求可能成功也可能逾時，使用者體驗是「有時候行有時候不行」。</p>
<p>處理策略：</p>
<ul>
<li>設定合理的逾時時間（太短會把慢回應判定為失敗，太長讓使用者等太久）</li>
<li>逾時後顯示狀態和重試選項，不自動重試（避免在不穩定網路上累積重試）</li>
<li>在 loading 狀態提供取消選項，讓使用者可以中斷等待</li>
</ul>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>Gate 設計的通用方法論 → <a href="/blog/ux-design/02-gate-fallback/gate-three-questions/" data-link-title="Gate 分類與三問設計法" data-link-desc="每個 gate 設計時問三個問題：成功時做什麼、失敗時做什麼、使用者不知道發生什麼時做什麼">Gate 分類與三問設計法</a></li>
<li>權限請求的 UX 設計 → <a href="/blog/ux-design/02-gate-fallback/permission-request-timing/" data-link-title="Permission 請求時機與措辭" data-link-desc="系統權限請求的時機選擇（首次開啟 vs 功能使用時）和說明文字的設計 — 使用者只有一次機會理解為什麼需要這個權限">Permission 請求時機與措辭</a></li>
<li>畫面狀態矩陣中的網路狀態 → <a href="/blog/ux-design/01-screen-state-machine/" data-link-title="模組一：畫面狀態機設計" data-link-desc="畫面狀態矩陣（顯示 / 操作 / 進入 / 退出）— 退出路徑為空 = UX 死胡同">ux-design 模組一 畫面狀態機</a></li>
<li>Server 端背壓如何影響 client UX → <a href="/blog/devops/03-traffic-management/backpressure/" data-link-title="背壓機制" data-link-desc="下游處理慢時上游怎麼減速 — 有限 buffer &#43; 回壓訊號的設計、和 rate limit 的區別">DevOps 背壓機制</a></li>
</ul>
]]></content:encoded></item><item><title>Permission 請求時機與措辭</title><link>https://tarrragon.github.io/blog/ux-design/02-gate-fallback/permission-request-timing/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ux-design/02-gate-fallback/permission-request-timing/</guid><description>&lt;p>系統權限（相機、位置、通知、麥克風）的請求對話框由作業系統控制，app 只能決定「什麼時候觸發」和「觸發前顯示什麼說明」。使用者拒絕後，再次請求不會彈出系統對話框 — 必須引導使用者到系統設定手動開啟。這意味著第一次請求的時機和說明內容直接影響授權率。&lt;/p>
&lt;h2 id="請求時機">請求時機&lt;/h2>
&lt;h3 id="首次開啟時一次性請求">首次開啟時一次性請求&lt;/h3>
&lt;p>App 首次啟動時依序請求所有需要的權限。優點是使用者只被打斷一次；缺點是使用者尚未使用任何功能，不理解每個權限的用途，傾向拒絕。&lt;/p>
&lt;p>這個模式適合權限數量少（1-2 個）且和 app 核心功能直接相關的情境。相機 app 在首次開啟時請求相機權限，使用者能直覺理解原因。&lt;/p>
&lt;h3 id="功能使用時即時請求">功能使用時即時請求&lt;/h3>
&lt;p>使用者點擊需要權限的功能時才請求。優點是使用者在操作 context 中，能理解為什麼需要這個權限；缺點是操作流程被打斷。&lt;/p>
&lt;p>這個模式適合權限和特定功能綁定的情境。掃描 QR code 時請求相機權限，使用者正在嘗試掃描，理解為什麼需要相機。&lt;/p>
&lt;h3 id="推薦策略">推薦策略&lt;/h3>
&lt;p>功能使用時即時請求是多數場景的推薦策略。使用者有操作 context，授權率較高。打斷可以透過 pre-permission 說明畫面降低突兀感。&lt;/p>
&lt;h2 id="pre-permission-說明畫面">Pre-permission 說明畫面&lt;/h2>
&lt;p>在觸發系統權限對話框之前，app 先顯示自己的說明畫面，解釋為什麼需要這個權限和用途。&lt;/p>
&lt;p>說明畫面的設計要點：&lt;/p>
&lt;p>&lt;strong>說明用途而非技術細節&lt;/strong>。「需要相機來掃描裝置上的 QR code」比「app 需要存取 AVCaptureDevice」更有用。使用者關心的是「為什麼」，不是「用什麼 API」。&lt;/p>
&lt;p>&lt;strong>提供「稍後再說」選項&lt;/strong>。使用者可能想先了解 app 再決定是否授權。強制授權（沒有跳過選項）會讓使用者選擇拒絕。&lt;/p>
&lt;p>&lt;strong>視覺化說明&lt;/strong>。用截圖或圖示展示「授權後這個功能長什麼樣」，讓使用者預覽授權的價值。&lt;/p>
&lt;h2 id="拒絕後的處理">拒絕後的處理&lt;/h2>
&lt;p>使用者拒絕權限後，app 需要：&lt;/p>
&lt;p>&lt;strong>記住拒絕狀態&lt;/strong>。不要在每次使用者操作同一功能時都顯示 pre-permission 說明（使用者已經表達不想授權，反覆詢問是騷擾）。&lt;/p>
&lt;p>&lt;strong>提供功能降級&lt;/strong>。如果可能，提供不需要權限的替代方案。掃描 QR code 可以改成手動輸入配對碼。&lt;/p>
&lt;p>&lt;strong>在適當時機再提醒&lt;/strong>。使用者多次使用需要權限的功能但都因為沒有權限而失敗時，用非侵入式提示（Snackbar）說明「開啟相機權限可以使用掃描功能」加設定連結。&lt;/p>
&lt;p>&lt;strong>引導到系統設定&lt;/strong>。一旦使用者在系統對話框中選擇「不再詢問」（Android）或拒絕（iOS 拒絕後系統不再彈窗），唯一的路徑是引導使用者到系統設定手動開啟。提供直接跳轉到 app 設定頁面的按鈕。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>Gate 設計的通用方法論 → &lt;a href="https://tarrragon.github.io/blog/ux-design/02-gate-fallback/gate-three-questions/" data-link-title="Gate 分類與三問設計法" data-link-desc="每個 gate 設計時問三個問題：成功時做什麼、失敗時做什麼、使用者不知道發生什麼時做什麼">Gate 分類與三問設計法&lt;/a>&lt;/li>
&lt;li>網路 gate 的處理策略 → &lt;a href="https://tarrragon.github.io/blog/ux-design/02-gate-fallback/network-offline-ux/" data-link-title="網路斷線 UX 模式" data-link-desc="Offline-first / retry / degraded mode 三種網路 gate 的處理策略 — 取決於功能是否依賴即時連線">網路斷線 UX 模式&lt;/a>&lt;/li>
&lt;li>開發環境遮蔽 gate 問題 → &lt;a href="https://tarrragon.github.io/blog/ux-design/02-gate-fallback/dev-vs-real-gate-behavior/" data-link-title="開發環境 vs 真機的 gate 行為差異表" data-link-desc="模擬器、debug build、test 環境中的 gate 行為和真機 release build 不同 — 差異表讓開發者在上機前知道哪些 gate 還沒被真實驗證">開發環境 vs 真機的 gate 行為差異表&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>系統權限（相機、位置、通知、麥克風）的請求對話框由作業系統控制，app 只能決定「什麼時候觸發」和「觸發前顯示什麼說明」。使用者拒絕後，再次請求不會彈出系統對話框 — 必須引導使用者到系統設定手動開啟。這意味著第一次請求的時機和說明內容直接影響授權率。</p>
<h2 id="請求時機">請求時機</h2>
<h3 id="首次開啟時一次性請求">首次開啟時一次性請求</h3>
<p>App 首次啟動時依序請求所有需要的權限。優點是使用者只被打斷一次；缺點是使用者尚未使用任何功能，不理解每個權限的用途，傾向拒絕。</p>
<p>這個模式適合權限數量少（1-2 個）且和 app 核心功能直接相關的情境。相機 app 在首次開啟時請求相機權限，使用者能直覺理解原因。</p>
<h3 id="功能使用時即時請求">功能使用時即時請求</h3>
<p>使用者點擊需要權限的功能時才請求。優點是使用者在操作 context 中，能理解為什麼需要這個權限；缺點是操作流程被打斷。</p>
<p>這個模式適合權限和特定功能綁定的情境。掃描 QR code 時請求相機權限，使用者正在嘗試掃描，理解為什麼需要相機。</p>
<h3 id="推薦策略">推薦策略</h3>
<p>功能使用時即時請求是多數場景的推薦策略。使用者有操作 context，授權率較高。打斷可以透過 pre-permission 說明畫面降低突兀感。</p>
<h2 id="pre-permission-說明畫面">Pre-permission 說明畫面</h2>
<p>在觸發系統權限對話框之前，app 先顯示自己的說明畫面，解釋為什麼需要這個權限和用途。</p>
<p>說明畫面的設計要點：</p>
<p><strong>說明用途而非技術細節</strong>。「需要相機來掃描裝置上的 QR code」比「app 需要存取 AVCaptureDevice」更有用。使用者關心的是「為什麼」，不是「用什麼 API」。</p>
<p><strong>提供「稍後再說」選項</strong>。使用者可能想先了解 app 再決定是否授權。強制授權（沒有跳過選項）會讓使用者選擇拒絕。</p>
<p><strong>視覺化說明</strong>。用截圖或圖示展示「授權後這個功能長什麼樣」，讓使用者預覽授權的價值。</p>
<h2 id="拒絕後的處理">拒絕後的處理</h2>
<p>使用者拒絕權限後，app 需要：</p>
<p><strong>記住拒絕狀態</strong>。不要在每次使用者操作同一功能時都顯示 pre-permission 說明（使用者已經表達不想授權，反覆詢問是騷擾）。</p>
<p><strong>提供功能降級</strong>。如果可能，提供不需要權限的替代方案。掃描 QR code 可以改成手動輸入配對碼。</p>
<p><strong>在適當時機再提醒</strong>。使用者多次使用需要權限的功能但都因為沒有權限而失敗時，用非侵入式提示（Snackbar）說明「開啟相機權限可以使用掃描功能」加設定連結。</p>
<p><strong>引導到系統設定</strong>。一旦使用者在系統對話框中選擇「不再詢問」（Android）或拒絕（iOS 拒絕後系統不再彈窗），唯一的路徑是引導使用者到系統設定手動開啟。提供直接跳轉到 app 設定頁面的按鈕。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>Gate 設計的通用方法論 → <a href="/blog/ux-design/02-gate-fallback/gate-three-questions/" data-link-title="Gate 分類與三問設計法" data-link-desc="每個 gate 設計時問三個問題：成功時做什麼、失敗時做什麼、使用者不知道發生什麼時做什麼">Gate 分類與三問設計法</a></li>
<li>網路 gate 的處理策略 → <a href="/blog/ux-design/02-gate-fallback/network-offline-ux/" data-link-title="網路斷線 UX 模式" data-link-desc="Offline-first / retry / degraded mode 三種網路 gate 的處理策略 — 取決於功能是否依賴即時連線">網路斷線 UX 模式</a></li>
<li>開發環境遮蔽 gate 問題 → <a href="/blog/ux-design/02-gate-fallback/dev-vs-real-gate-behavior/" data-link-title="開發環境 vs 真機的 gate 行為差異表" data-link-desc="模擬器、debug build、test 環境中的 gate 行為和真機 release build 不同 — 差異表讓開發者在上機前知道哪些 gate 還沒被真實驗證">開發環境 vs 真機的 gate 行為差異表</a></li>
</ul>
]]></content:encoded></item><item><title>開發環境 vs 真機的 gate 行為差異表</title><link>https://tarrragon.github.io/blog/ux-design/02-gate-fallback/dev-vs-real-gate-behavior/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ux-design/02-gate-fallback/dev-vs-real-gate-behavior/</guid><description>&lt;p>開發環境遮蔽 gate 問題的機制是：模擬器或 debug build 中的 gate 行為比真機寬鬆，讓問題在開發階段不可見，直到實機測試或 production 才浮現。這和 mock 遮蔽 protocol 問題的機制結構相同（&lt;a href="https://tarrragon.github.io/blog/testing/01-test-strategy-layers/" data-link-title="模組一：測試策略分層" data-link-desc="Unit / Protocol Integration / Screen State 三層測試各自的職責、盲區和判斷原則">testing 模組一&lt;/a>）— 開發環境的「寬鬆模式」讓功能缺失變得不可見。&lt;/p>
&lt;h2 id="差異機制">差異機制&lt;/h2>
&lt;h3 id="模擬器不支援硬體功能">模擬器不支援硬體功能&lt;/h3>
&lt;p>iOS 模擬器不支援 Face ID / Touch ID 硬體。&lt;code>local_auth&lt;/code> 的 &lt;code>isAvailable()&lt;/code> 在模擬器上回傳 &lt;code>false&lt;/code>（&lt;code>isDeviceSupported()&lt;/code> 為 &lt;code>true&lt;/code> 但 &lt;code>getAvailableBiometrics()&lt;/code> 為空），app 跳過認證走預設路徑。&lt;/p>
&lt;p>在真機上 &lt;code>isAvailable()&lt;/code> 回傳 &lt;code>true&lt;/code>，app 嘗試認證，如果設定了 &lt;code>biometricOnly: true&lt;/code> 且 Face ID 失敗，使用者被擋住。模擬器上「跳過認證直接使用」的體驗讓開發者以為認證流程沒有問題（&lt;a href="https://tarrragon.github.io/blog/ux-design/cases/biometric-only-no-fallback/" data-link-title="U.C2 biometricOnly=true 無密碼 fallback" data-link-desc="Flutter app 的生物辨識設定 biometricOnly: true 阻擋所有非生物辨識認證方式 — Face ID 不可用時使用者直接被擋住，沒有替代路徑">U.C2&lt;/a>）。&lt;/p>
&lt;h3 id="debug-build-的權限行為不同">Debug build 的權限行為不同&lt;/h3>
&lt;p>某些平台在 debug build 和 release build 的權限處理不同。例如 Android 的某些 OEM 客製化系統在 debug mode 下自動授予特定權限，release mode 下需要手動授權。&lt;/p>
&lt;h3 id="test-環境跳過-gate">Test 環境跳過 gate&lt;/h3>
&lt;p>Unit test 和 integration test 通常 mock 掉所有 gate — &lt;code>FakeBiometricService&lt;/code> 永遠回傳成功，&lt;code>FakeNetworkChecker&lt;/code> 永遠回傳已連線。這和名義 integration test 的問題相同 — test 環境的「一切正常」遮蔽了真實環境的 gate 失敗場景。&lt;/p>
&lt;h2 id="gate-行為差異表">Gate 行為差異表&lt;/h2>
&lt;p>在功能規格中建立一張差異表，列出每個 gate 在不同環境下的行為差異：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Gate&lt;/th>
 &lt;th>模擬器行為&lt;/th>
 &lt;th>真機 debug&lt;/th>
 &lt;th>真機 release&lt;/th>
 &lt;th>風險&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>生物辨識&lt;/td>
 &lt;td>跳過（硬體不可用）&lt;/td>
 &lt;td>可測試（需設定）&lt;/td>
 &lt;td>正常&lt;/td>
 &lt;td>模擬器上看不到 fallback 缺失&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>網路連線&lt;/td>
 &lt;td>通常正常（host 網路）&lt;/td>
 &lt;td>可斷 WiFi 測試&lt;/td>
 &lt;td>行動網路 + WiFi&lt;/td>
 &lt;td>模擬器的網路狀態不代表行動網路&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>相機權限&lt;/td>
 &lt;td>無相機（或虛擬相機）&lt;/td>
 &lt;td>可測試&lt;/td>
 &lt;td>正常&lt;/td>
 &lt;td>模擬器無法測試真實權限流程&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>藍牙&lt;/td>
 &lt;td>不支援&lt;/td>
 &lt;td>可測試&lt;/td>
 &lt;td>正常&lt;/td>
 &lt;td>模擬器完全跳過藍牙相關功能&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Push 通知&lt;/td>
 &lt;td>不支援（iOS 模擬器）&lt;/td>
 &lt;td>可測試&lt;/td>
 &lt;td>正常&lt;/td>
 &lt;td>通知觸發的導航路徑在模擬器不可測&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>App 簽名驗證&lt;/td>
 &lt;td>debug 簽名自動通過&lt;/td>
 &lt;td>debug 簽名&lt;/td>
 &lt;td>release 簽名&lt;/td>
 &lt;td>簽名相關的 gate 只在 release 生效&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="差異表的使用方式">差異表的使用方式&lt;/h2>
&lt;h3 id="開發階段">開發階段&lt;/h3>
&lt;p>開發者對照差異表，意識到哪些 gate 在當前環境下沒有被真實驗證。差異表中「模擬器行為」和「真機 release」不同的行 = 需要上真機確認的項目。&lt;/p>
&lt;h3 id="實機測試規劃">實機測試規劃&lt;/h3>
&lt;p>測試計畫中針對差異表的每一行設計測試案例。生物辨識的測試案例必須涵蓋「Face ID 失敗時的 fallback」，網路連線的測試案例必須涵蓋「飛航模式下的 UX」。&lt;/p>
&lt;h3 id="code-review">Code review&lt;/h3>
&lt;p>Review 涉及 gate 的程式碼時，對照差異表確認 fallback 路徑是否存在。如果 review 的程式碼用了 &lt;code>biometricOnly: true&lt;/code>，差異表立刻提示「模擬器上看不到這個問題，需要上真機確認 fallback」。&lt;/p>
&lt;p>差異表揭露的問題和 testing 領域的 mock 遮蔽在結構上相同 — &lt;a href="https://tarrragon.github.io/blog/testing/01-test-strategy-layers/mock-masking-mechanism/" data-link-title="Mock 遮蔽機制分析" data-link-desc="Mock 在 API 層、協議層、環境層之間製造的結構性盲區 — 斷裂點在哪、為什麼 mock 無法也不應該模擬協議行為">testing 模組一 Mock 遮蔽機制&lt;/a>從 API 層 vs 協議層的角度分析同一類問題。差異表本身是&lt;a href="https://tarrragon.github.io/blog/ux-design/02-gate-fallback/gate-three-questions/" data-link-title="Gate 分類與三問設計法" data-link-desc="每個 gate 設計時問三個問題：成功時做什麼、失敗時做什麼、使用者不知道發生什麼時做什麼">三問設計法&lt;/a>在實機驗證階段的延伸，biometric gate 的完整 fallback 設計見 &lt;a href="https://tarrragon.github.io/blog/ux-design/02-gate-fallback/biometric-fallback-design/" data-link-title="Biometric fallback 完整設計" data-link-desc="iOS Face ID / Touch ID 和 Android BiometricPrompt 的行為差異、fallback 策略、安全 vs 可用性取捨的顯式記錄方法">Biometric fallback 完整設計&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>開發環境遮蔽 gate 問題的機制是：模擬器或 debug build 中的 gate 行為比真機寬鬆，讓問題在開發階段不可見，直到實機測試或 production 才浮現。這和 mock 遮蔽 protocol 問題的機制結構相同（<a href="/blog/testing/01-test-strategy-layers/" data-link-title="模組一：測試策略分層" data-link-desc="Unit / Protocol Integration / Screen State 三層測試各自的職責、盲區和判斷原則">testing 模組一</a>）— 開發環境的「寬鬆模式」讓功能缺失變得不可見。</p>
<h2 id="差異機制">差異機制</h2>
<h3 id="模擬器不支援硬體功能">模擬器不支援硬體功能</h3>
<p>iOS 模擬器不支援 Face ID / Touch ID 硬體。<code>local_auth</code> 的 <code>isAvailable()</code> 在模擬器上回傳 <code>false</code>（<code>isDeviceSupported()</code> 為 <code>true</code> 但 <code>getAvailableBiometrics()</code> 為空），app 跳過認證走預設路徑。</p>
<p>在真機上 <code>isAvailable()</code> 回傳 <code>true</code>，app 嘗試認證，如果設定了 <code>biometricOnly: true</code> 且 Face ID 失敗，使用者被擋住。模擬器上「跳過認證直接使用」的體驗讓開發者以為認證流程沒有問題（<a href="/blog/ux-design/cases/biometric-only-no-fallback/" data-link-title="U.C2 biometricOnly=true 無密碼 fallback" data-link-desc="Flutter app 的生物辨識設定 biometricOnly: true 阻擋所有非生物辨識認證方式 — Face ID 不可用時使用者直接被擋住，沒有替代路徑">U.C2</a>）。</p>
<h3 id="debug-build-的權限行為不同">Debug build 的權限行為不同</h3>
<p>某些平台在 debug build 和 release build 的權限處理不同。例如 Android 的某些 OEM 客製化系統在 debug mode 下自動授予特定權限，release mode 下需要手動授權。</p>
<h3 id="test-環境跳過-gate">Test 環境跳過 gate</h3>
<p>Unit test 和 integration test 通常 mock 掉所有 gate — <code>FakeBiometricService</code> 永遠回傳成功，<code>FakeNetworkChecker</code> 永遠回傳已連線。這和名義 integration test 的問題相同 — test 環境的「一切正常」遮蔽了真實環境的 gate 失敗場景。</p>
<h2 id="gate-行為差異表">Gate 行為差異表</h2>
<p>在功能規格中建立一張差異表，列出每個 gate 在不同環境下的行為差異：</p>
<table>
  <thead>
      <tr>
          <th>Gate</th>
          <th>模擬器行為</th>
          <th>真機 debug</th>
          <th>真機 release</th>
          <th>風險</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>生物辨識</td>
          <td>跳過（硬體不可用）</td>
          <td>可測試（需設定）</td>
          <td>正常</td>
          <td>模擬器上看不到 fallback 缺失</td>
      </tr>
      <tr>
          <td>網路連線</td>
          <td>通常正常（host 網路）</td>
          <td>可斷 WiFi 測試</td>
          <td>行動網路 + WiFi</td>
          <td>模擬器的網路狀態不代表行動網路</td>
      </tr>
      <tr>
          <td>相機權限</td>
          <td>無相機（或虛擬相機）</td>
          <td>可測試</td>
          <td>正常</td>
          <td>模擬器無法測試真實權限流程</td>
      </tr>
      <tr>
          <td>藍牙</td>
          <td>不支援</td>
          <td>可測試</td>
          <td>正常</td>
          <td>模擬器完全跳過藍牙相關功能</td>
      </tr>
      <tr>
          <td>Push 通知</td>
          <td>不支援（iOS 模擬器）</td>
          <td>可測試</td>
          <td>正常</td>
          <td>通知觸發的導航路徑在模擬器不可測</td>
      </tr>
      <tr>
          <td>App 簽名驗證</td>
          <td>debug 簽名自動通過</td>
          <td>debug 簽名</td>
          <td>release 簽名</td>
          <td>簽名相關的 gate 只在 release 生效</td>
      </tr>
  </tbody>
</table>
<h2 id="差異表的使用方式">差異表的使用方式</h2>
<h3 id="開發階段">開發階段</h3>
<p>開發者對照差異表，意識到哪些 gate 在當前環境下沒有被真實驗證。差異表中「模擬器行為」和「真機 release」不同的行 = 需要上真機確認的項目。</p>
<h3 id="實機測試規劃">實機測試規劃</h3>
<p>測試計畫中針對差異表的每一行設計測試案例。生物辨識的測試案例必須涵蓋「Face ID 失敗時的 fallback」，網路連線的測試案例必須涵蓋「飛航模式下的 UX」。</p>
<h3 id="code-review">Code review</h3>
<p>Review 涉及 gate 的程式碼時，對照差異表確認 fallback 路徑是否存在。如果 review 的程式碼用了 <code>biometricOnly: true</code>，差異表立刻提示「模擬器上看不到這個問題，需要上真機確認 fallback」。</p>
<p>差異表揭露的問題和 testing 領域的 mock 遮蔽在結構上相同 — <a href="/blog/testing/01-test-strategy-layers/mock-masking-mechanism/" data-link-title="Mock 遮蔽機制分析" data-link-desc="Mock 在 API 層、協議層、環境層之間製造的結構性盲區 — 斷裂點在哪、為什麼 mock 無法也不應該模擬協議行為">testing 模組一 Mock 遮蔽機制</a>從 API 層 vs 協議層的角度分析同一類問題。差異表本身是<a href="/blog/ux-design/02-gate-fallback/gate-three-questions/" data-link-title="Gate 分類與三問設計法" data-link-desc="每個 gate 設計時問三個問題：成功時做什麼、失敗時做什麼、使用者不知道發生什麼時做什麼">三問設計法</a>在實機驗證階段的延伸，biometric gate 的完整 fallback 設計見 <a href="/blog/ux-design/02-gate-fallback/biometric-fallback-design/" data-link-title="Biometric fallback 完整設計" data-link-desc="iOS Face ID / Touch ID 和 Android BiometricPrompt 的行為差異、fallback 策略、安全 vs 可用性取捨的顯式記錄方法">Biometric fallback 完整設計</a>。</p>
]]></content:encoded></item></channel></rss>