Flutter Device orientation for single page on iOS

2,314

Solution 1

I've had the exact same requirement in one of my apps. Lucky you - the whole project is open sourced! Let's get it done:

  1. Add the platform channel logic on the iOS side in ios/Runner/AppDelegate.m - https://github.com/vintage/party_flutter/blob/27b11fc46755d8901f02c3b439b294ca9005277a/ios/Runner/AppDelegate.m#L8-L23
#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;

    FlutterMethodChannel* rotationChannel = [FlutterMethodChannel
                                             methodChannelWithName:@"zgadula/orientation"
                                             binaryMessenger:controller];

    [rotationChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
        if ([@"setLandscape" isEqualToString:call.method]) {
            [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeRight) forKey:@"orientation"];
        }
        else if ([@"setPortrait" isEqualToString:call.method]) {
            [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortrait) forKey:@"orientation"];
        }
        else {
            result(FlutterMethodNotImplemented);
        }
    }];

    [GeneratedPluginRegistrant registerWithRegistry:self];
    // Override point for customization after application launch.
    return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

@end
  1. In the widget/screen which should be landscape oriented define the MethodChannel - https://github.com/vintage/party_flutter/blob/master/lib/ui/screens/game_play.dart#L34
static const _rotationChannel = const MethodChannel('zgadula/orientation');

3.initState should trigger the rotation to landscape - https://github.com/vintage/party_flutter/blob/master/lib/ui/screens/game_play.dart#L71-L78

SystemChrome.setPreferredOrientations([
  DeviceOrientation.landscapeRight,
]);
// TODO: Remove it when fixed in Flutter
// https://github.com/flutter/flutter/issues/13238
try {
  _rotationChannel.invokeMethod('setLandscape');
} catch (error) {}

The try-catch is to handle Android part (no such channel, as it works as expected without it).

  1. When disposing - rotate back to the portrait mode - https://github.com/vintage/party_flutter/blob/master/lib/ui/screens/game_play.dart#L111-L122

SystemChrome.setPreferredOrientations([
  DeviceOrientation.portraitUp,
]);

// TODO: Remove it when fixed in Flutter
// https://github.com/flutter/flutter/issues/13238
try {
  _rotationChannel.invokeMethod('setPortrait');
} catch (error) {}

if (_rotateSubscription != null) {
  _rotateSubscription.cancel();
}

Feel free to change zgadula/orientation to something which would match your project better :)

Solution 2

In my case, I made it without channels. Only Flutter code.

1st step. Change possible device orientations in the Xcode

enter image description here

2nd step. In the root widget of your app add a list of default orientations. Usually, it's before the Material app widget.

@override
  void initState() {
    SystemChrome.setPreferredOrientations([
      DeviceOrientation.portraitUp,
    ]);
    super.initState();
  }

So, now, our app has only one orientation.

3rd step. On the screen that should have other orientations (for example, in my case, full-screen videos) add

 @override
 void initState() {
   SystemChrome.setPreferredOrientations([
     DeviceOrientation.portraitUp,
     DeviceOrientation.landscapeRight,
     DeviceOrientation.landscapeLeft,
   ]);
   super.initState();
 }
 @override
  void dispose() {
    SystemChrome.setPreferredOrientations([
      DeviceOrientation.portraitUp,
    ]);
    super.dispose();
  }
Share:
2,314
NullPointer
Author by

NullPointer

Updated on December 07, 2022

Comments

  • NullPointer
    NullPointer over 1 year

    I want to show a single page in my Flutter application in landscape mode. Every other screen should be shown in portrait mode.

    I found this code snippet:

    In my main.dart

    SystemChrome.setPreferredOrientations([
        DeviceOrientation.portraitUp,
        DeviceOrientation.portraitDown,
      ]).then((_) {
        runApp(new IHGApp());
      });
    

    This starts the app in portrait mode. So I have the screen I want to show in landscape mode and this is the code I used there:

    @override
      void initState() {
        super.initState();
        SystemChrome.setPreferredOrientations([
          DeviceOrientation.landscapeRight,
          DeviceOrientation.landscapeLeft,
        ]);
      }
    
    
      @override
      void dispose() {
        SystemChrome.setPreferredOrientations([
          DeviceOrientation.portraitUp,
          DeviceOrientation.portraitDown,
        ]);
        super.dispose();
      }
    

    This works on Android.

    On iOS it seems there is no way to force landscape mode for a single page.

    https://github.com/flutter/flutter/issues/13238

    On this article I found the issue for this problem. sroddy mentioned how to fix the problem.

    "I workarounded the issue creating a small platform channel that invokes this code for switching to portrait right before the call to setPreferredOrientations:"

    [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortrait) forKey:@"orientation"];
    

    And the counterpart code for switching to landscape

    [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeLeft) forKey:@"orientation"];
    

    How can I implement this in my app?

  • Shangari C
    Shangari C over 4 years
    I tried method channel but when I rotate to landscape its crashing.Please suggest me any ides.