使用 Flutter 開發跨平台應用程式,就像掌握了一種魔法 ✨。透過單一程式碼基礎,你就能在 Android 和 iOS 上部署豐富且美觀的應用。但每個有經驗的 Flutter 開發者都知道,這份魔法偶爾也會出現些小脾氣 —— 尤其是當你在 iOS 裝置上處理媒體播放時。有個令人頭痛的問題,就是媒體時長(duration)在播放過程中無法正確更新。這個問題隱晦、不穩定,卻會在用戶回報播放器控制功能異常時讓你陷入一場找不到出口的迷霧 🎧。
對 Samir 來說,他是一位正在開發正念應用程式的行動開發工程師,一切在 Android 上都運行得完美。使用者能夠串流冥想課程、暫停播放、拖曳時間軸,還能看到準確的時長顯示。但在 iOS 上,播放器元件卻顯示時長為零,有時甚至是 null,不論音訊檔實際長度如何。一開始他以為是資源路徑錯誤或是音訊格式問題,但即使更換了多種 .mp3
和 .m4a
檔案後,問題依然如影隨形。
這種問題通常牽涉到 Flutter 框架與 iOS 原生 AVFoundation 之間的溝通。Android 使用 ExoPlayer 提供相對穩定的播放回饋,但 iOS 則需要更多的手動調整。Samir 最終發現,媒體項目的 duration
屬性之所以無法正確回傳,是因為 AVPlayerItem 尚未完成讀取完整的媒體 metadata。若 Flutter plugin 沒有等待這個過程結束,就會提前讀取一個錯誤的或尚未解析的時間資訊。
在 Flutter 插件世界中,just_audio
和 video_player
是常見的播放套件,它們通常會幫你抽象化底層的原生平台邏輯,但當 OS 層級行為出現細節差異時,這些套件也可能讓問題變得更隱晦。在 iOS 上,AVPlayer 可能需要幾秒的時間來解析資源 metadata,才會發送實際的 duration。如果你的 Flutter 播放器沒有明確監聽播放器是否 ready,那麼這些資訊就會錯過。
Maria 是一位維護語言學習應用的開發者。她的團隊花了數個月整合離線音檔與書籤功能,以及時間戳導覽。整體使用體驗在 Android 上非常流暢,但在 iOS 發佈後,使用者回報無法拖曳至特定段落,時間軸顯示也不正常。她後來才注意到,在 Android 上 audioPlayer.durationStream
會即時發出值,而在 iOS 上這個值有時需要等待更長的緩衝時間。為了處理這個問題,他們使用 StreamBuilder
動態監聽 duration 並延後呈現 UI,直到獲得非 null 值。
這問題也與應用的生命週期事件有關。在 iOS 上,如果播放過程中遇到電話來電、Siri 啟動,或是系統記憶體不足等情況,duration 有可能會被重設或停止更新。有些開發者在開發支援背景播放的 podcast 應用時發現,當應用從背景返回前景後,duration 更新停止,但實際播放仍在進行。這種情況下,就需要重新監聽 AVPlayer 狀態,甚至重新初始化 session。
當播放來源是遠端 URL 時,事情會變得更複雜。如果音訊是從 CDN 或需要認證的伺服器取得,iOS 的 metadata 載入速度會受到檔案 header、快取策略、或 MIME 類型影響。Clara 在開發一款串流健身課程的應用時發現,特定 URL 的音檔在 iOS 上持續出現 duration 問題,後來查出原因是伺服器沒有穩定傳送 Content-Length
header,導致 AVPlayer 載入流程延誤。一旦後端團隊修正這些 header,問題就迎刃而解 📡。
此外,音訊編碼格式也會影響 duration 判斷。雖然 iOS 對 .mp3
和 .mp4
支援良好,但對 .ogg
、.flac
或格式異常的音訊檔則可能無法正確解析 metadata。這也是許多開發者習慣將所有音檔轉為 H.264/AAC 格式的原因,這樣才能確保 iOS 的媒體播放穩定無誤。
在除錯這類問題時,僅靠 Flutter 的 DevTools 通常不足以深入問題核心。你可能需要透過 Xcode 的除錯主控台,觀察 AVFoundation 的 log,甚至需要些 Swift 或 Objective-C 的知識來撰寫測試。許多開發者會先用 native AVPlayer 撰寫小型播放器測試,確認問題根源,再回到 Flutter plugin 作出對應調整。這聽起來繁瑣,但若你的應用是商業產品,這些細節將直接影響使用者留存。
對使用者而言,這不是個小問題。UI 顯示瑕疵可能忍一下就過了,但時間軸不動、播放控制失效、進度記錄不準,這些會直接影響使用者的信任感。一位正在做冥想或健身課程的用戶,絕對不希望在使用過程中猜測還剩幾分鐘,或者因為拖曳失效被迫重聽整段內容 🧘。
但值得慶幸的是,Flutter 社群一直以來都非常強大且樂於分享。你可以在 GitHub、Stack Overflow 或 Reddit 上看到無數開發者分享他們解決類似問題的經驗。有些套件甚至會在更新紀錄中幽默地註記:「修復 iOS 17.2 AVPlayerItem race condition」、「為低速網路加入延遲以等待 duration sync」—— 每一條都是一位開發者的血與淚。
Samir 最終也不是用一行程式碼解決了問題,而是調整了使用者體驗:他在 UI 上加入了簡單的 loading 動畫,讓使用者知道播放器正在準備中;當 duration 尚未可用時,禁用時間軸操作;等到資料回傳後,再釋放這些控制元件。他發現,使用者並不介意等個兩三秒,他們在乎的是應用有沒有「交代清楚」這段時間在做什麼。
這些經驗告訴我們,Flutter 雖然強大,也並非萬能。你仍然需要了解背後平台的邏輯、針對細節做出微調。而當你真的解開一個複雜的跨平台問題時,那種成就感,是任何框架文件中都找不到的 💡。因為那一刻,不只是程式動起來了,而是你對技術與使用者的理解,又深了一層。
留言
發佈留言