iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🍭

Managing Screen Orientation on a Per-Screen Basis in Flutter

に公開

While I have some doubts about whether changing whether rotation is possible or not on a per-screen basis is good for usability, I've confirmed it works on both iOS and Android, so I'll leave a note here.

Steps

Native Project Settings

In the native settings for iOS and Android, allow all orientations for screen rotation.
By default, all orientations should be allowed, so if you haven't made any specific changes, no action is required.

Flutter Implementation

Add a class for monitoring screen transitions to navigatorObservers in MaterialApp. Within this class, check the destination screen and change the allowed orientation settings for each rotation.

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const HomeScreeen(),
      navigatorObservers: [
        // Class for monitoring screen transitions
        MyNavigatorObserver(),
      ],
    );
  }
}

Implement the class for monitoring screen transitions as follows:

my_navigator_observer.dart
class MyNavigatorObserver extends NavigatorObserver {
  @override
  void didPush(Route route, Route? previousRoute) {
    // When navigating to a new screen
    super.didPush(route, previousRoute);

    if (route.settings.name == RotatableScreen.name) {
      // For screens that allow rotation
      SystemChrome.setPreferredOrientations([
        // Register portrait and landscape as allowed orientations
        DeviceOrientation.portraitUp,
        DeviceOrientation.landscapeLeft,
        DeviceOrientation.landscapeRight,
      ]);
    } else {
      // For screens that do not allow rotation
      SystemChrome.setPreferredOrientations([
        // Register only portrait as the allowed orientation
        DeviceOrientation.portraitUp,
      ]);
    }
  }

  @override
  void didPop(Route route, Route? previousRoute) {
    // When returning to the previous screen
    super.didPop(route, previousRoute);

    if (route.settings.name == RotatableScreen.name) {
      // For screens that allow rotation
      SystemChrome.setPreferredOrientations([
        DeviceOrientation.portraitUp,
        DeviceOrientation.landscapeLeft,
        DeviceOrientation.landscapeRight,
      ]);
    } else {
      SystemChrome.setPreferredOrientations([
        DeviceOrientation.portraitUp,
      ]);
    }
  }
}

There are various ways to determine whether or not a screen is a target for allowing rotation. In the example above, I use the value set in RouteSettings.name for the determination, assuming that a unique string is assigned to it when calling Navigator.push during screen transitions.

// Screen transition process
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),
      );

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

References

Discussion