Files
SpotiFLAC-Mobile/lib/services/app_orientation.dart
T
zarzet 2ab4aaa8dc fix(ui): allow landscape only in the full player
Keep application pages in portrait and let player sheets inherit rotation from the full player. Restore portrait on navigation, replacement, removal, and completed dismissal; discard stale minimization geometry when the screen rotates on close.

Validated with 12 Flutter tests and targeted analysis. iOS simulator build confirms portrait startup from a landscape device. Platform orientation restrictions still apply on large Android displays and multitasking iPads.
2026-09-25 18:05:40 +07:00

72 lines
1.9 KiB
Dart

import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:spotiflac_android/utils/logger.dart';
const fullPlayerRouteName = '/full-player';
final _log = AppLogger('Orientation');
/// Only the full player opts into rotation. Sheets and dialogs inherit the
/// underlying page's orientation, while another page restores portrait.
Future<void> setAppOrientation({bool playerVisible = false}) =>
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
if (playerVisible) ...[
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
],
]);
class AppOrientationObserver extends NavigatorObserver {
final _routes = <Route<dynamic>>[];
bool? _playerVisible;
void _sync() {
final page = _routes.whereType<PageRoute<dynamic>>().lastOrNull;
final playerVisible = page?.settings.name == fullPlayerRouteName;
if (_playerVisible == playerVisible) return;
_playerVisible = playerVisible;
unawaited(
setAppOrientation(playerVisible: playerVisible).catchError((
Object error,
) {
_log.w('Could not update screen orientation: $error');
}),
);
}
@override
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
_routes.add(route);
_sync();
}
@override
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
_routes.remove(route);
_sync();
}
@override
void didRemove(Route<dynamic> route, Route<dynamic>? previousRoute) {
_routes.remove(route);
_sync();
}
@override
void didReplace({Route<dynamic>? newRoute, Route<dynamic>? oldRoute}) {
final index = oldRoute == null ? -1 : _routes.indexOf(oldRoute);
if (index >= 0) {
if (newRoute == null) {
_routes.removeAt(index);
} else {
_routes[index] = newRoute;
}
} else if (newRoute != null) {
_routes.add(newRoute);
}
_sync();
}
}