<?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>App-Link on Tarragon</title><link>https://tarrragon.github.io/blog/tags/app-link/</link><description>Recent content in App-Link 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/app-link/index.xml" rel="self" type="application/rss+xml"/><item><title>Deep link 設計</title><link>https://tarrragon.github.io/blog/ux-design/05-navigation-patterns/deep-link-design/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ux-design/05-navigation-patterns/deep-link-design/</guid><description>&lt;p>Deep link 讓 app 外部的來源（網頁連結、推播通知、其他 app）直接導航到 app 的特定畫面，而非每次都從首頁開始。Deep link 的設計需要考慮三個問題：URL 結構如何對應到畫面、app 未安裝時怎麼處理、導航堆疊如何重建。&lt;/p>
&lt;h2 id="三種-deep-link-機制">三種 deep link 機制&lt;/h2>
&lt;h3 id="custom-url-scheme">Custom URL scheme&lt;/h3>
&lt;p>App 註冊自訂的 URL scheme（&lt;code>myapp://&lt;/code>），系統收到這個 scheme 的 URL 時打開 app。&lt;code>myapp://terminal?host=192.168.1.100&lt;/code> 打開 app 的 terminal 畫面。&lt;/p>
&lt;p>Custom URL scheme 的限制：沒有 ownership 驗證（任何 app 都可以註冊 &lt;code>myapp://&lt;/code>），只在 app 已安裝時有效（未安裝時 URL 無效），不適合 web 分享（瀏覽器無法開啟 &lt;code>myapp://&lt;/code>）。&lt;/p>
&lt;h3 id="universal-linkios-app-linkandroid">Universal Link（iOS）/ App Link（Android）&lt;/h3>
&lt;p>App 宣告擁有特定 domain 的 URL（&lt;code>https://example.com/terminal&lt;/code>）。系統驗證 domain 的 ownership（domain 上放 &lt;code>.well-known/apple-app-site-association&lt;/code> 或 &lt;code>assetlinks.json&lt;/code>），驗證通過後這些 URL 直接在 app 中打開。&lt;/p>
&lt;p>優勢：使用標準 HTTPS URL（可以在瀏覽器中分享）、有 ownership 驗證（防止冒充）、app 未安裝時 fallback 到網頁。&lt;/p>
&lt;h3 id="deferred-deep-link">Deferred deep link&lt;/h3>
&lt;p>使用者點擊 deep link 時 app 未安裝。系統引導使用者到 app store 安裝，安裝後首次開啟時自動導航到 deep link 指定的畫面。&lt;/p>
&lt;p>Deferred deep link 需要第三方服務（Firebase Dynamic Links、Branch）或自建機制在安裝前後傳遞 URL 參數。&lt;/p>
&lt;h2 id="url-結構設計">URL 結構設計&lt;/h2>
&lt;p>Deep link 的 URL 結構應該和 GoRouter 的路由定義一致。GoRouter 原生支援 deep link — URL path 就是路由 path。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">https://example.com/terminal → TerminalScreen
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">https://example.com/enrollment → EnrollmentScreen
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">https://example.com/terminal?host=x → TerminalScreen(host: x)&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>URL 參數（query parameters）傳遞畫面需要的資料。參數值避免包含敏感資訊 — URL 可能被系統日誌、分析工具、中間人記錄。&lt;/p>
&lt;h2 id="導航堆疊重建">導航堆疊重建&lt;/h2>
&lt;p>使用者從 deep link 直接進入 &lt;code>/terminal&lt;/code> 畫面時，導航堆疊中沒有首頁。使用者按 back 應該回到首頁還是離開 app？&lt;/p>
&lt;h3 id="重建完整堆疊">重建完整堆疊&lt;/h3>
&lt;p>GoRouter 的 &lt;code>go('/terminal')&lt;/code> 可以設定為自動把前置路由放入堆疊。使用者按 back 回到首頁，再按 back 離開 app。使用者的心理模型是「deep link 帶我到這個畫面，back 帶我到 app 的正常入口」。&lt;/p>
&lt;h3 id="只放-deep-link-目標">只放 deep link 目標&lt;/h3>
&lt;p>堆疊中只有 deep link 目標畫面。按 back 離開 app。適合「一次性操作」的 deep link（打開 → 操作 → 離開）。&lt;/p>
&lt;h3 id="選擇策略">選擇策略&lt;/h3>
&lt;p>如果 deep link 的畫面是 app 日常使用的一部分，重建完整堆疊讓使用者能繼續在 app 中操作。如果 deep link 是從外部觸發的獨立操作（掃描 QR code → 顯示結果），只放目標畫面更簡潔。&lt;/p>
&lt;h2 id="deep-link-測試">Deep link 測試&lt;/h2>
&lt;p>Deep link 需要端對端測試 — 從外部觸發 URL，驗證 app 導航到正確畫面。&lt;/p></description><content:encoded><![CDATA[<p>Deep link 讓 app 外部的來源（網頁連結、推播通知、其他 app）直接導航到 app 的特定畫面，而非每次都從首頁開始。Deep link 的設計需要考慮三個問題：URL 結構如何對應到畫面、app 未安裝時怎麼處理、導航堆疊如何重建。</p>
<h2 id="三種-deep-link-機制">三種 deep link 機制</h2>
<h3 id="custom-url-scheme">Custom URL scheme</h3>
<p>App 註冊自訂的 URL scheme（<code>myapp://</code>），系統收到這個 scheme 的 URL 時打開 app。<code>myapp://terminal?host=192.168.1.100</code> 打開 app 的 terminal 畫面。</p>
<p>Custom URL scheme 的限制：沒有 ownership 驗證（任何 app 都可以註冊 <code>myapp://</code>），只在 app 已安裝時有效（未安裝時 URL 無效），不適合 web 分享（瀏覽器無法開啟 <code>myapp://</code>）。</p>
<h3 id="universal-linkios-app-linkandroid">Universal Link（iOS）/ App Link（Android）</h3>
<p>App 宣告擁有特定 domain 的 URL（<code>https://example.com/terminal</code>）。系統驗證 domain 的 ownership（domain 上放 <code>.well-known/apple-app-site-association</code> 或 <code>assetlinks.json</code>），驗證通過後這些 URL 直接在 app 中打開。</p>
<p>優勢：使用標準 HTTPS URL（可以在瀏覽器中分享）、有 ownership 驗證（防止冒充）、app 未安裝時 fallback 到網頁。</p>
<h3 id="deferred-deep-link">Deferred deep link</h3>
<p>使用者點擊 deep link 時 app 未安裝。系統引導使用者到 app store 安裝，安裝後首次開啟時自動導航到 deep link 指定的畫面。</p>
<p>Deferred deep link 需要第三方服務（Firebase Dynamic Links、Branch）或自建機制在安裝前後傳遞 URL 參數。</p>
<h2 id="url-結構設計">URL 結構設計</h2>
<p>Deep link 的 URL 結構應該和 GoRouter 的路由定義一致。GoRouter 原生支援 deep link — URL path 就是路由 path。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">https://example.com/terminal        → TerminalScreen
</span></span><span class="line"><span class="ln">2</span><span class="cl">https://example.com/enrollment      → EnrollmentScreen
</span></span><span class="line"><span class="ln">3</span><span class="cl">https://example.com/terminal?host=x → TerminalScreen(host: x)</span></span></code></pre></div><p>URL 參數（query parameters）傳遞畫面需要的資料。參數值避免包含敏感資訊 — URL 可能被系統日誌、分析工具、中間人記錄。</p>
<h2 id="導航堆疊重建">導航堆疊重建</h2>
<p>使用者從 deep link 直接進入 <code>/terminal</code> 畫面時，導航堆疊中沒有首頁。使用者按 back 應該回到首頁還是離開 app？</p>
<h3 id="重建完整堆疊">重建完整堆疊</h3>
<p>GoRouter 的 <code>go('/terminal')</code> 可以設定為自動把前置路由放入堆疊。使用者按 back 回到首頁，再按 back 離開 app。使用者的心理模型是「deep link 帶我到這個畫面，back 帶我到 app 的正常入口」。</p>
<h3 id="只放-deep-link-目標">只放 deep link 目標</h3>
<p>堆疊中只有 deep link 目標畫面。按 back 離開 app。適合「一次性操作」的 deep link（打開 → 操作 → 離開）。</p>
<h3 id="選擇策略">選擇策略</h3>
<p>如果 deep link 的畫面是 app 日常使用的一部分，重建完整堆疊讓使用者能繼續在 app 中操作。如果 deep link 是從外部觸發的獨立操作（掃描 QR code → 顯示結果），只放目標畫面更簡潔。</p>
<h2 id="deep-link-測試">Deep link 測試</h2>
<p>Deep link 需要端對端測試 — 從外部觸發 URL，驗證 app 導航到正確畫面。</p>
<p>測試項目：</p>
<ul>
<li>每個路由的 deep link 能正確打開</li>
<li>URL 參數正確傳遞到畫面</li>
<li>App 在前景、背景、未啟動三種狀態下都能處理 deep link</li>
<li>無效的 deep link URL 有合理的 fallback（導航到首頁或顯示錯誤）</li>
<li>Universal Link 的 domain verification 正確</li>
</ul>
<p>Deep link 的實作在 Flutter 中由 GoRouter 的 route matching 處理 — <a href="/blog/ux-design/05-navigation-patterns/flutter-gorouter/" data-link-title="Flutter GoRouter 導航設計" data-link-desc="GoRouter 的路由定義、導航 API（go / push / pushReplacement）、redirect 機制和 ShellRoute 的使用場景">Flutter GoRouter 導航設計</a>包含 deep link 的設定方式。Deep link 觸發的導航操作（go vs push）影響使用者的返回路徑，語意差異見 <a href="/blog/ux-design/05-navigation-patterns/go-push-semantics/" data-link-title="go vs push vs pushReplacement 的 UX 語意表" data-link-desc="三種導航方法對堆疊、back 行為、使用者心理模型的影響 — 選擇依據是使用者的意圖而非技術方便">go vs push vs pushReplacement 語意表</a>。Deep link 的端對端驗證在 <a href="/blog/testing/04-ui-automation/" data-link-title="模組四：自動化 UI 驗證" data-link-desc="Widget test 的狀態覆蓋策略、Playwright 驗證流程、螢幕狀態 coverage">testing 模組四 自動化 UI 驗證</a>中歸類到導航路徑 test。</p>
]]></content:encoded></item></channel></rss>