Mobile Development 15 min read

Creating an iOS Flutter Audio Player Plugin with MethodChannel

This tutorial explains step‑by‑step how to create a custom iOS Flutter plugin for audio playback, covering the MethodChannel architecture, Dart side implementation, Swift native code, and communication of playback controls and status between Flutter and iOS.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Creating an iOS Flutter Audio Player Plugin with MethodChannel

Flutter’s vision is that a developer can write a single codebase and run it on multiple platforms. Although many third‑party plugins exist, custom features such as a proprietary Bluetooth protocol often require a native plugin. This article shows how to implement a Flutter plugin that controls audio playback on iOS.

Prerequisites

The iOS project must be opened with Xcode. The example builds a music player with functions for play, pause, resume, seek, and duration retrieval, all exposed through a native plugin.

Architecture Overview

Flutter and native code communicate via a MethodChannel . The Dart side sends method calls, and the iOS side receives them, performs the native operation, and invokes callbacks to report progress, duration, completion, or errors.

Flutter Side – Dart Implementation

A singleton AudioPlayer class defines a MethodChannel named "netmusic.com/audio_player" . It provides methods that invoke native calls ( play , pause , resume , seek ) and registers a handler to receive callbacks from iOS.

class AudioPlayer {
// singleton
factory AudioPlayer() => _getInstance();
static AudioPlayer _instance;
AudioPlayer._internal() {
channel.setMethodCallHandler(nativePlatformCallHandler);
}
static const channel = MethodChannel("netmusic.com/audio_player");
// ... other members and methods (play, pause, resume, seek, etc.)
Future
nativePlatformCallHandler(MethodCall call) async {
final args = call.arguments as Map
;
switch (call.method) {
case "onPosition":
// update current position
break;
case "onDuration":
// update total duration
break;
case "onComplete":
// handle completion
break;
case "onError":
// handle error
break;
}
}
}

iOS Side – Swift Implementation

The AppDelegate creates a PlayerWrapper instance and passes the FlutterViewController to it. PlayerWrapper sets up a FlutterMethodChannel with the same name and registers a handler for incoming method calls.

import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
var playerWrapper: PlayerWrapper?
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let controller = window?.rootViewController as! FlutterViewController
playerWrapper = PlayerWrapper(vc: controller)
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
import Foundation
import Flutter
import AVKit
import CoreMedia
class PlayerWrapper: NSObject {
var vc: FlutterViewController
var channel: FlutterMethodChannel
var player: AVPlayer?
init(vc: FlutterViewController) {
self.vc = vc
channel = FlutterMethodChannel(name: "netmusic.com/audio_player", binaryMessenger: vc.binaryMessenger)
super.init()
channel.setMethodCallHandler(handleFlutterMessage)
>  }
func handleFlutterMessage(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
let method = call.method
let args = call.arguments as? [String: Any]
>    if method == "play" {
guard let urlString = args?["url"] as? String, let url = URL(string: urlString) else { result(0); return }
>      player?.pause()
>      let asset = AVAsset(url: url)
>      let item = AVPlayerItem(asset: asset)
>      player = AVPlayer(playerItem: item)
>      player?.play()
>      // periodic time observer to report position
>      player?.addPeriodicTimeObserver(forInterval: CMTimeMake(value: 1, timescale: 1), queue: nil) { [weak self] time in
>        self?.channel.invokeMethod("onPosition", arguments: ["value": time.value / Int64(time.timescale)])
>      }
>      // observe status for duration and errors
>      item.observe(\AVPlayerItem.status) { [weak self] playerItem, _ in
>        if playerItem.status == .readyToPlay {
>          if let duration = self?.player?.currentItem?.asset.duration {
>            self?.channel.invokeMethod("onDuration", arguments: ["value": duration.value / Int64(duration.timescale)])
>          }
>        } else if playerItem.status == .failed {
>          self?.channel.invokeMethod("onError", arguments: ["value": "play failed"])
>        }
>      }
>      // completion notification
>      NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: item, queue: nil) { [weak self] _ in
>        self?.channel.invokeMethod("onComplete", arguments: [])
>      }
>      result(1)
>    } else if method == "pause" || method == "stop" {
player?.pause()
>      result(1)
>    } else if method == "resume" {
player?.play()
>      result(1)
>    } else if method == "seek" {
guard let position = args?["position"] as? Int else { result(0); return }
>      let seekTime = CMTimeMake(value: Int64(position), timescale: 1)
>      player?.seek(to: seekTime)
>    }
>  }
>}

Flutter Receiving Callbacks from iOS

The iOS side invokes onPosition , onDuration , onComplete , and onError . The Dart nativePlatformCallHandler updates streams ( onCurrentTimeChanged , onTotalTimeChanged , etc.) which can be consumed by StreamBuilder widgets to reflect playback state in the UI.

StreamBuilder(
  stream: AudioPlayer().onTotalTimeChanged,
  builder: (context, snapshot) {
    if (!snapshot.hasData) return Text("00:00");
    return Text(AudioPlayer().totalPlayTimeStr);
  },
)

With the above pieces, a functional audio player plugin is assembled. The article concludes by linking to the full source code and mentions that a follow‑up will cover the Android counterpart.

DartFlutteriOSpluginSwiftMethodChannelAudioPlayer
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.