From 84dd903bc44182d1ec020236699c2a82ef86064d Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Thu, 12 Nov 2026 14:09:13 -0500 Subject: [PATCH] more gracefully handle failed audio messages --- lib/widgets/players/audio.dart | 75 ++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 22 deletions(-) diff --git a/lib/widgets/players/audio.dart b/lib/widgets/players/audio.dart index 419d0ed..5d06d59 100644 --- a/lib/widgets/players/audio.dart +++ b/lib/widgets/players/audio.dart @@ -1,6 +1,7 @@ import "dart:async"; import "package:audioplayers/audioplayers.dart" as audioplayers; +import "package:flutter/services.dart"; import "package:material_ui/material_ui.dart"; import "package:flutter_hooks/flutter_hooks.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; @@ -20,6 +21,7 @@ class const AudioPlayer( Widget build(BuildContext context, WidgetRef ref) { final player = useMemoized(audioplayers.AudioPlayer.new); final loaded = useState(false); + final unsupported = useState(false); useEffect(() { scheduleMicrotask(() async { @@ -29,7 +31,16 @@ class const AudioPlayer( if (!context.mounted) return; // Keeps the audio loaded after it finishes, so it can be replayed await player.setReleaseMode(.stop); - await player.setSource(audioplayers.DeviceFileSource(audio.path)); + try { + // AVFoundation infers the format from the extension, which the cached file lacks + await player.setSource( + audioplayers.DeviceFileSource(audio.path, mimeType: info?.mimeType), + ); + } on PlatformException catch (_) { + // The platform can't play this format, like Ogg on Apple platforms + if (context.mounted) unsupported.value = true; + return; + } if (context.mounted) loaded.value = true; }); @@ -50,32 +61,52 @@ class const AudioPlayer( ).requireData; final playing = state == .playing; + final downloadButton = IconButton( + onPressed: () => context.saveAttachment( + ref, + uri, + encrypted: encrypted, + filename: filename, + ), + tooltip: "Download", + icon: Icon(Icons.download), + ); + return Card( color: Theme.of(context).colorScheme.surfaceContainerHigh, child: Padding( padding: .symmetric(horizontal: 8), - child: PlayerControls( - playing: playing, - position: position, - duration: duration, - onPlayPause: playing ? player.pause : player.resume, - onSeek: player.seek, - loading: !loaded.value, - size: info?.size, - actions: .new([ - SizedBox(width: 4), - IconButton( - onPressed: () => context.saveAttachment( - ref, - uri, - encrypted: encrypted, - filename: filename, + child: unsupported.value + ? Row( + children: [ + Padding( + padding: .all(12), + child: Icon( + Icons.music_off, + color: Theme.of(context).colorScheme.error, + ), + ), + Expanded( + child: Text( + "Can't play this audio format", + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context).colorScheme.error, + ), + ), + ), + downloadButton, + ], + ) + : PlayerControls( + playing: playing, + position: position, + duration: duration, + onPlayPause: playing ? player.pause : player.resume, + onSeek: player.seek, + loading: !loaded.value, + size: info?.size, + actions: .new([SizedBox(width: 4), downloadButton]), ), - tooltip: "Download", - icon: Icon(Icons.download), - ), - ]), - ), ), ); }