iOS WebView Won't Play Spotify Audio Content in Flutter App

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?

Yeah, this is a known iOS issue - nothing wrong with your Flutter code. Apple’s WKWebView policies block media playback in WebViews unless it’s from trusted sources or meets their security requirements. Spotify adds their own restrictions on top of that to control content distribution.

I’ve seen a hybrid approach work well: detect if you’re on iOS and handle it differently. Keep the WebView for browsing and metadata, but when someone tries to play audio on iOS, intercept that and redirect them to Spotify’s Web Playback SDK with a custom implementation. More setup work, but you get native-like audio controls without fighting iOS security. The trick is splitting content browsing from actual playback on iOS.

This iOS audio blocking thing hits everyone eventually. I’ve dealt with similar issues across multiple apps and honestly, trying to work around WebView restrictions is a time sink.

Step back and automate the whole audio delivery pipeline instead of fighting with WebViews. I built something that monitors Spotify’s Web API for track changes, automatically pulls preview URLs, and streams them through a native audio player. The automation handles all the API calls, caching, and even preloads the next tracks.

You can set up workflows that automatically detect when a user wants to play audio, grab the preview from Spotify’s API in the background, and serve it through Flutter’s native audio players. No WebView headaches, no iOS restrictions, and it works consistently across platforms.

I use Latenode to orchestrate the whole thing. It connects to Spotify’s API, manages the audio URLs, and triggers the appropriate playback actions in your Flutter app through webhooks. Takes about an hour to set up versus weeks of fighting WebView policies.

Check it out here: https://latenode.com

Just hit this same issue last month. iOS WebView audio is a nightmare. Here’s what worked for me - switch to WKWebView, set allowsInlineMediaPlayback to true and clear the mediaTypesRequiringUserActionForPlayback array. Not bulletproof but kills the app redirect problem and basic previews work fine.

yeah, that’s just how ios webviews work - pretty annoying. spotify blocks webview playback on ios for security reasons. try flutter_inappwebview instead. it handles ios audio way better than the standard webview_flutter package and gives you more control over permissions.

Hit this same wall six months ago and wasted hours debugging before I figured out it’s not Flutter’s fault. iOS WebView blocks Spotify content because it thinks it’s embedded media that needs user interaction, while Spotify’s web player sees WebView as sketchy and won’t cooperate.

Here’s what actually worked: I built a fallback system that injects JavaScript when the WebView loads on iOS. It checks if Spotify redirects to their app install page instead of playing audio. When that happens, I grab the track metadata and pull preview URLs straight from Spotify’s Web API. Then I feed those URLs to Flutter’s just_audio plugin for native playback.

Honestly, the native audio controls work way better than the web player anyway. Takes some setup but kills all WebView audio problems for good.

Had this exact problem last year and wasted weeks trying different fixes. It’s not your Flutter code - Spotify deliberately blocks WebView audio on iOS to protect their content. They want everyone using their official app where they control the DRM and login stuff.

What finally worked: ditch the embedded web player and use Spotify’s Web API instead. Pull the track previews through their API and play them with Flutter’s audioplayers package. Those 30-second preview URLs work great in native players without any WebView headaches. You’ll need Spotify Developer credentials, but it’s easy to set up and works way better across platforms.