Flutter Device orientation for single page on iOS
Asked Answered
T

3

8

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?

Telesthesia answered 24/10, 2018 at 23:3 Comment(0)
C
3

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 :)

Crepitate answered 25/7, 2019 at 12:13 Comment(1)
I tried method channel but when I rotate to landscape its crashing.Please suggest me any ides.Smasher
R
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();
  }
Rouge answered 3/3, 2021 at 8:31 Comment(0)
P
0

In my case, I find it better to restore default values inside a WillPopScope widget instead of dispose, because you'll get a smoother pop transition (but may not be valid for all cases)

Prescott answered 19/2, 2022 at 10:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.