<?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>Key on Tarragon</title><link>https://tarrragon.github.io/blog/tags/key/</link><description>Recent content in Key on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Tue, 30 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/key/index.xml" rel="self" type="application/rss+xml"/><item><title>Widget 子類重新宣告 key — 遮蔽父類屬性與 duplicate key 風險</title><link>https://tarrragon.github.io/blog/work-log/widget-%E5%AD%90%E9%A1%9E%E9%87%8D%E6%96%B0%E5%AE%A3%E5%91%8A-key-%E9%81%AE%E8%94%BD%E7%88%B6%E9%A1%9E%E5%B1%AC%E6%80%A7%E8%88%87-duplicate-key-%E9%A2%A8%E9%9A%AA/</link><pubDate>Tue, 30 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/work-log/widget-%E5%AD%90%E9%A1%9E%E9%87%8D%E6%96%B0%E5%AE%A3%E5%91%8A-key-%E9%81%AE%E8%94%BD%E7%88%B6%E9%A1%9E%E5%B1%AC%E6%80%A7%E8%88%87-duplicate-key-%E9%A2%A8%E9%9A%AA/</guid><description>&lt;h2 id="事件">事件&lt;/h2>
&lt;p>測試用的 &lt;code>TestRiveAnimation extends StatelessWidget&lt;/code> 裡宣告了 &lt;code>final Key? key;&lt;/code>，constructor 中透過 &lt;code>super(key: key)&lt;/code> 傳給父類。Dart analyzer 警告 &lt;code>key&lt;/code> overrides an inherited member。&lt;/p>
&lt;p>加了 &lt;code>@override&lt;/code> 可以消除警告，但問題沒有解決——class 裡現在有兩個 &lt;code>key&lt;/code> slot（子類自己的和 &lt;code>Widget&lt;/code> 繼承的），而 &lt;code>build&lt;/code> 方法裡又寫了 &lt;code>Container(key: key)&lt;/code>，把同一個 key 同時掛在 parent widget 和 child widget 上。&lt;/p>
&lt;h2 id="根因">根因&lt;/h2>
&lt;p>&lt;code>Widget&lt;/code> 的 &lt;code>key&lt;/code> 是 &lt;code>final&lt;/code> 屬性，由 constructor 的 &lt;code>super(key:)&lt;/code> 設定。子類重新宣告同名欄位會產生 shadowing：&lt;/p>
&lt;ul>
&lt;li>子類的程式碼（包括 &lt;code>build&lt;/code>）讀到的是子類自己的那份 &lt;code>key&lt;/code>&lt;/li>
&lt;li>父類 &lt;code>Widget&lt;/code> 的框架程式碼讀到的是父類的那份 &lt;code>key&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>兩份值相同（因為 constructor 都有寫入），但語意上是兩個獨立的 slot。更危險的是，如果在 &lt;code>build&lt;/code> 裡把 &lt;code>key&lt;/code> 往下傳給 child，同一棵 widget 子樹會出現兩個相同的 &lt;code>Key&lt;/code> 值，Flutter 在 diff 時可能拋出 duplicate key 錯誤。&lt;/p>
&lt;h2 id="修法">修法&lt;/h2>
&lt;p>不要重新宣告 &lt;code>key&lt;/code>，改用 &lt;code>super.key&lt;/code>：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-dart" data-lang="dart">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="kd">const&lt;/span> &lt;span class="n">TestRiveAnimation&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">asset&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">asset&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl"> &lt;span class="k">super&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// 直接傳給 Widget，不產生新 slot
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">useArtboardSize&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="p">});&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>build&lt;/code> 裡也不要把 widget 自身的 key 再傳給 child——key 是給 framework 用來識別這個 widget 的，不該手動轉發。&lt;/p>
&lt;h2 id="判斷原則">判斷原則&lt;/h2>
&lt;p>在 Flutter 中，&lt;code>key&lt;/code>、&lt;code>hashCode&lt;/code>、&lt;code>runtimeType&lt;/code> 這類從 &lt;code>Widget&lt;/code> / &lt;code>Object&lt;/code> 繼承的屬性，子類永遠不該用欄位覆蓋。如果需要自訂行為，覆寫 getter。&lt;/p></description><content:encoded><![CDATA[<h2 id="事件">事件</h2>
<p>測試用的 <code>TestRiveAnimation extends StatelessWidget</code> 裡宣告了 <code>final Key? key;</code>，constructor 中透過 <code>super(key: key)</code> 傳給父類。Dart analyzer 警告 <code>key</code> overrides an inherited member。</p>
<p>加了 <code>@override</code> 可以消除警告，但問題沒有解決——class 裡現在有兩個 <code>key</code> slot（子類自己的和 <code>Widget</code> 繼承的），而 <code>build</code> 方法裡又寫了 <code>Container(key: key)</code>，把同一個 key 同時掛在 parent widget 和 child widget 上。</p>
<h2 id="根因">根因</h2>
<p><code>Widget</code> 的 <code>key</code> 是 <code>final</code> 屬性，由 constructor 的 <code>super(key:)</code> 設定。子類重新宣告同名欄位會產生 shadowing：</p>
<ul>
<li>子類的程式碼（包括 <code>build</code>）讀到的是子類自己的那份 <code>key</code></li>
<li>父類 <code>Widget</code> 的框架程式碼讀到的是父類的那份 <code>key</code></li>
</ul>
<p>兩份值相同（因為 constructor 都有寫入），但語意上是兩個獨立的 slot。更危險的是，如果在 <code>build</code> 裡把 <code>key</code> 往下傳給 child，同一棵 widget 子樹會出現兩個相同的 <code>Key</code> 值，Flutter 在 diff 時可能拋出 duplicate key 錯誤。</p>
<h2 id="修法">修法</h2>
<p>不要重新宣告 <code>key</code>，改用 <code>super.key</code>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dart" data-lang="dart"><span class="line"><span class="ln">1</span><span class="cl"><span class="kd">const</span> <span class="n">TestRiveAnimation</span><span class="p">.</span><span class="n">asset</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">  <span class="k">this</span><span class="p">.</span><span class="n">asset</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="k">super</span><span class="p">.</span><span class="n">key</span><span class="p">,</span>           <span class="c1">// 直接傳給 Widget，不產生新 slot
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"></span>  <span class="k">this</span><span class="p">.</span><span class="n">useArtboardSize</span> <span class="o">=</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">});</span></span></span></code></pre></div><p><code>build</code> 裡也不要把 widget 自身的 key 再傳給 child——key 是給 framework 用來識別這個 widget 的，不該手動轉發。</p>
<h2 id="判斷原則">判斷原則</h2>
<p>在 Flutter 中，<code>key</code>、<code>hashCode</code>、<code>runtimeType</code> 這類從 <code>Widget</code> / <code>Object</code> 繼承的屬性，子類永遠不該用欄位覆蓋。如果需要自訂行為，覆寫 getter。</p>
]]></content:encoded></item></channel></rss>