I’m working on a Flutter app that shows Spotify playlists inside a WebView component. The problem is really frustrating because it works perfectly on Android devices but fails completely on iOS.
What works: On Android, users can tap any song and hear the preview audio right away.
What doesn’t work: On iOS, tapping songs just shows a page asking users to install the Spotify mobile app instead of playing the audio.
I’m not sure if this is caused by my implementation or if it’s related to iOS security restrictions that block audio playback in WebViews. I’ve tested this with webview_flutter versions 2.4.0 and 4.5.0 but got the same results.
Here’s my implementation:
class _PlaylistViewState extends State<PlaylistView> {
final Completer<WebViewController> _webController =
Completer<WebViewController>();
ValueNotifier<bool> loadingState = ValueNotifier(false);
@override
void initState() {
super.initState();
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
Timer(const Duration(seconds: 2), () {
_webController.future.then((controller) {
controller.reload();
});
});
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Material(
child: WillPopScope(
onWillPop: () {
_webController.future.then((controller) {
controller.goBack();
});
return Future.value(false);
},
child: Scaffold(
appBar: AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Audio Player',
style: Theme.of(context).textTheme.headline6?.copyWith(
color: AppColors.primaryWhite,
),
),
const ProfileWidget(),
],
),
),
body: Stack(
children: [
WebView(
initialUrl: 'https://open.spotify.com/intl-en/artist/4Z8W4fKeB5YxbusRsdQVPb',
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController controller) {
_webController.complete(controller);
},
javascriptChannels: <JavascriptChannel>[
_messageChannel(context),
].toSet(),
onPageStarted: (String url) {
debugPrint('Loading started: $url');
},
onPageFinished: (String url) {
debugPrint('Loading completed: $url');
},
gestureNavigationEnabled: true,
onProgress: (progress) {
if (progress < 100) {
loadingState.value = true;
} else {
loadingState.value = false;
}
},
),
ValueListenableBuilder<bool>(
valueListenable: loadingState,
builder: (context, isLoading, _) {
return Center(
child: Visibility(
visible: isLoading,
child: const CircularProgressIndicator(),
),
);
},
),
ValueListenableBuilder<bool>(
valueListenable: loadingState,
builder: (context, isLoading, _) {
return Positioned(
bottom: 16,
left: 16,
child: Container(
decoration: const BoxDecoration(
color: AppColors.accentColor,
shape: BoxShape.circle,
),
child: Visibility(
visible: !isLoading,
child: IconButton(
onPressed: () {
_webController.future.then((controller) {
controller.reload();
});
},
icon: const Icon(
Icons.refresh,
color: AppColors.primaryWhite,
),
),
),
),
);
},
),
],
),
),
),
),
);
}
JavascriptChannel _messageChannel(BuildContext context) {
return JavascriptChannel(
name: 'MessageHandler',
onMessageReceived: (JavascriptMessage msg) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(msg.message)),
);
});
}
}
Any ideas on how to make audio playback work on iOS?