From 09860f81c8e2b5a862fc512e99f80272dd1af6bc Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Wed, 3 Dec 2025 11:14:59 +0100 Subject: [PATCH] fix(ios): player rotation --- ios/Runner/MpvPlayer/MpvPlayerPlugin.swift | 10 ++++++++++ lib/mpv/src/player/player.dart | 7 +++++++ lib/mpv/src/player/player_native.dart | 11 +++++++++++ lib/screens/video_player_screen.dart | 16 ++++++++-------- macos/Runner/MpvPlayer/MpvPlayerPlugin.swift | 10 ++++++++++ 5 files changed, 46 insertions(+), 8 deletions(-) diff --git a/ios/Runner/MpvPlayer/MpvPlayerPlugin.swift b/ios/Runner/MpvPlayer/MpvPlayerPlugin.swift index 1d6dae4a..3e81464d 100644 --- a/ios/Runner/MpvPlayer/MpvPlayerPlugin.swift +++ b/ios/Runner/MpvPlayer/MpvPlayerPlugin.swift @@ -76,6 +76,9 @@ class MpvPlayerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, MpvPlayerD case "isInitialized": result(playerCore?.isInitialized ?? false) + case "updateFrame": + handleUpdateFrame(result: result) + default: result(FlutterMethodNotImplemented) } @@ -198,6 +201,13 @@ class MpvPlayerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, MpvPlayerD } } + private func handleUpdateFrame(result: @escaping FlutterResult) { + DispatchQueue.main.async { [weak self] in + self?.playerCore?.updateFrame() + result(nil) + } + } + // MARK: - MpvPlayerDelegate func onPropertyChange(name: String, value: Any?) { diff --git a/lib/mpv/src/player/player.dart b/lib/mpv/src/player/player.dart index b7bcef56..e817ac86 100644 --- a/lib/mpv/src/player/player.dart +++ b/lib/mpv/src/player/player.dart @@ -178,6 +178,13 @@ abstract class Player { /// controls are hidden. On other platforms, this is a no-op. Future setControlsVisible(bool visible); + /// Update the video frame/surface dimensions. + /// + /// On iOS/macOS, this updates the Metal layer's frame to match the current + /// window size. Call this when the layout changes (e.g., device rotation). + /// On other platforms, this is a no-op. + Future updateFrame(); + // ============================================ // Lifecycle // ============================================ diff --git a/lib/mpv/src/player/player_native.dart b/lib/mpv/src/player/player_native.dart index d1db58b0..c2b95841 100644 --- a/lib/mpv/src/player/player_native.dart +++ b/lib/mpv/src/player/player_native.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io' show Platform; import 'package:flutter/services.dart'; @@ -491,6 +492,16 @@ class PlayerNative implements Player { // No-op on most platforms. Override on Linux for transparency workaround. } + @override + Future updateFrame() async { + _checkDisposed(); + if (!_initialized) return; + // Only iOS and macOS use Metal layer that needs frame updates + if (Platform.isIOS || Platform.isMacOS) { + await _methodChannel.invokeMethod('updateFrame'); + } + } + // ============================================ // Lifecycle // ============================================ diff --git a/lib/screens/video_player_screen.dart b/lib/screens/video_player_screen.dart index 2769e844..990c524d 100644 --- a/lib/screens/video_player_screen.dart +++ b/lib/screens/video_player_screen.dart @@ -1184,14 +1184,14 @@ class VideoPlayerScreenState extends State constraints.maxHeight, ); - // Update player size in video filter manager - if (_videoFilterManager != null) { - WidgetsBinding.instance.addPostFrameCallback((_) { - if (mounted) { - _videoFilterManager!.updatePlayerSize(newSize); - } - }); - } + // Update player size in video filter manager and native layer + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted && player != null) { + _videoFilterManager?.updatePlayerSize(newSize); + // Update Metal layer frame on iOS/macOS for rotation + player!.updateFrame(); + } + }); return Video( player: player!, diff --git a/macos/Runner/MpvPlayer/MpvPlayerPlugin.swift b/macos/Runner/MpvPlayer/MpvPlayerPlugin.swift index 38f88dce..38f7eb9c 100644 --- a/macos/Runner/MpvPlayer/MpvPlayerPlugin.swift +++ b/macos/Runner/MpvPlayer/MpvPlayerPlugin.swift @@ -76,6 +76,9 @@ class MpvPlayerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, MpvPlayerD case "isInitialized": result(playerCore?.isInitialized ?? false) + case "updateFrame": + handleUpdateFrame(result: result) + default: result(FlutterMethodNotImplemented) } @@ -198,6 +201,13 @@ class MpvPlayerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, MpvPlayerD } } + private func handleUpdateFrame(result: @escaping FlutterResult) { + DispatchQueue.main.async { [weak self] in + self?.playerCore?.updateFrame() + result(nil) + } + } + // MARK: - MpvPlayerDelegate func onPropertyChange(name: String, value: Any?) {