Media Player Plugin for NativePHP Mobile#
Audio/video playback for NativePHP Mobile: a shared background player driven from PHP, a full-screen system player, and an inline <video-player> element for native (Edge) views.
Overview#
The plugin provides three ways to play media:
- Shared player —
MediaPlayer::play()plays audio or video sources app-wide (AVPlayer on iOS, MediaPlayer on Android) with pause/resume/seek/volume control from PHP. - Full-screen presentation —
MediaPlayer::present()opens the system player UI (AVPlayerViewController on iOS, a dedicated full-screen activity on Android); the user dismisses it natively. - Inline
<video-player>element — a video surface for native UI views, rendered as SwiftUIVideoPlayer(AVKit) on iOS and aVideoViewinside Compose on Android.
Installation#
composer require nativephp/mobile-media-player
Don't forget to register the plugin:
php artisan native:plugin:register nativephp/mobile-media-player
Usage#
Shared player (PHP)#
use NativePHP\MediaPlayer\Facades\MediaPlayer; // Play a bundled file, storage path, or URLMediaPlayer::play(storage_path('app/theme.mp3')); // With optionsMediaPlayer::play('https://example.com/stream.mp3', [ 'loop' => true, 'volume' => 0.5,]); // Transport controlsMediaPlayer::pause();MediaPlayer::resume();MediaPlayer::seek(42.5); // secondsMediaPlayer::setVolume(0.8); // 0.0 – 1.0MediaPlayer::stop(); // stops and releases the player // Poll playback state$status = MediaPlayer::getStatus();// ['state' => 'playing', 'position' => 12.3, 'duration' => 180.0, 'source' => '...']
play() and present() return false when the source can't be started (or when
running outside the native runtime).
Full-screen system player#
MediaPlayer::present(storage_path('app/videos/intro.mp4'));
Inline video in native views#
<video-player :src="$video['path']" controls class="w-full aspect-video rounded-2xl"/>
Attributes:
| Attribute | Default | Description |
|---|---|---|
src |
— | File path or URL |
controls |
true |
Show native transport chrome |
autoplay |
false |
Start playback on mount |
loop |
false |
Restart when playback ends |
muted |
false |
Start muted |
Sizing follows the element's layout props (width/height/aspect classes), like Image.
With controls=false you get a bare video surface — overlay your own Element UI
and drive playback through the MediaPlayer facade:
<stack class="w-full aspect-video"> <video-player :src="$src" :controls="false" autoplay muted class="w-full h-full"/> <button @press="togglePlayback" icon="pause.fill" class="absolute bottom-2 right-2"/></stack>
Building the element from PHP#
use NativePHP\MediaPlayer\Elements\VideoPlayer; VideoPlayer::make($path) ->controls(false) ->autoplay() ->loop() ->muted();
Events#
PlaybackEnded#
Fired when the shared player reaches the end of the source.
Payload: string $source
use Native\Mobile\Attributes\OnNative;use NativePHP\MediaPlayer\Events\PlaybackEnded; #[OnNative(PlaybackEnded::class)]public function handlePlaybackEnded(string $source){ $this->playNext();}
PlaybackError#
Fired when the shared player fails to load or play a source.
Payload: string $source, string $message
use Native\Mobile\Attributes\OnNative;use NativePHP\MediaPlayer\Events\PlaybackError; #[OnNative(PlaybackError::class)]public function handlePlaybackError(string $source, string $message){ $this->status = "Playback failed: {$message}";}
Status values#
MediaPlayer::getStatus()['state'] is one of: idle, playing, paused.
Testing#
The plugin extends the NativePHP testing suite with playback-specific helpers, so your app tests can fake and assert media activity without knowing any bridge internals:
use Native\Mobile\Testing\Native; it('plays the selected track', function () { Native::fakeBridge(); Native::test(NowPlaying::class) ->tap('Play') ->assertPlayed('https://example.com/stream.mp3');}); it('shows where playback is', function () { Native::fakeBridge()->withPlaybackStatus('playing', position: 12.0, duration: 180.0); Native::test(NowPlaying::class) ->call('refresh') ->assertSee('0:12 / 3:00');}); it('pauses, seeks, and stops', function () { Native::fakeBridge(); Native::test(NowPlaying::class) ->tap('Pause')->assertPaused() ->tap('Skip forward')->assertSeeked(30.0) ->tap('Stop')->assertStopped();});
Helpers#
withPlaybackStatus(string $state = 'playing', float $position = 0.0, float $duration = 0.0, ?string $source = null)— script the statusgetStatus()reports.$stateis one ofidle,playing,paused.assertPlayed(?string $source = null)— assert playback was started, or exactly$source(a path or URL) when given.assertPaused()— assert playback was paused.assertStopped()— assert playback was stopped and the player released.assertSeeked(?float $to = null)— assert a seek happened, or to exactly$toseconds when given.assertNothingPlayed()— assert no playback was started.
The helpers are available on Native::fakeBridge() and chain directly off Native::test(...). They register automatically while running tests (requires a core with a macroable FakeBridge; on older cores they simply don't register).
Platform Support#
- iOS: 16.0+ (AVPlayer / AVKit)
- Android: API 26+ (MediaPlayer / Media3 UI)
Notes#
- One shared player: calling
play()replaces whatever is currently playing. - Remote URLs require network access; local paths must be readable by the app
(e.g.
storage_path()or bundled assets). - No runtime permissions are required.