🍭

Flutterで回転時に向きを変えられるか否かを画面ごとに切り替える

2021/11/12に公開

アプリ内で回転ができるか否かが画面毎に変わるのはユーザビリティ的にどうかと思う部分もありますが、一応iOS・Androidともに動作確認して問題なかったため、メモしておきます。

手順

ネイティブのプロジェクト設定

iOSとAndroidのネイティブにおける設定では、画面回転における全ての向きを許可しておきます。
デフォルトでは全ての向きが許可になっていると思いますので、特に変更していない場合は対応不要です。

Flutterの実装

MaterialAppにおけるnavigatorObserversに、画面遷移を監視するためのクラスを追加します。このクラス内で、遷移先の画面を確認して都度回転で許可する向き設定を変更します。

root_app.dart
class RootApp extends StatelessWidget {
  const RootApp({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const HomeScreeen(),
      navigatorObservers: [
        // 画面遷移を監視するためのクラス
        MyNavigatorObserver(),
      ],
    );
  }
}

画面遷移を監視するためのクラスは以下のように実装します。

my_navigator_observer.dart
class MyNavigatorObserver extends NavigatorObserver {
  
  void didPush(Route route, Route? previousRoute) {
    // 新しい画面に遷移したとき
    super.didPush(route, previousRoute);

    if (route.settings.name == RotatableScreen.name) {
      // 回転を許可する画面の場合
      SystemChrome.setPreferredOrientations([
        // 縦向きと横向きを許可する方向として登録する
        DeviceOrientation.portraitUp,
        DeviceOrientation.landscapeLeft,
        DeviceOrientation.landscapeRight,
      ]);
    } else {
      // 回転を許可しない画面の場合
      SystemChrome.setPreferredOrientations([
        // 縦向きのみを許可する方向として登録する
        DeviceOrientation.portraitUp,
      ]);
    }
  }

  
  void didPop(Route route, Route? previousRoute) {
    // 前の画面に戻ったとき
    super.didPop(route, previousRoute);

    if (route.settings.name == RotatableScreen.name) {
      // 回転を許可する画面の場合
      SystemChrome.setPreferredOrientations([
        DeviceOrientation.portraitUp,
        DeviceOrientation.landscapeLeft,
        DeviceOrientation.landscapeRight,
      ]);
    } else {
      SystemChrome.setPreferredOrientations([
        DeviceOrientation.portraitUp,
      ]);
    }
  }
}

回転を許可したい対象の画面であるか否かの判定はさまざまな方法があると思いますが、上記では、画面遷移でNavigator.pushを呼び出す際に、固有の文字列をRouteSettings.nameに付与するようにしておく前提で、その設定値を判定に利用しています。

// 画面遷移処理
Navigator.push<void>(context, RotatableScreen.route());

// RotatableScreen
class RotatableScreen extends StatelessWidget {
  const RotatableScreen({Key? key}) : super(key: key);

  static const name = 'RotatableScreen';

  static MaterialPageRoute route() => MaterialPageRoute<RotatableScreen>(
        builder: (_) => const RotatableScreen(),
        settings: const RouteSettings(name: name),
      );

  
  Widget build(BuildContext context) {
  // ...

参考

Discussion