Controlling screen orientation in Flutter apps on a per-screen basis
While working on the new Flutter app, I needed to make some screens appear in portrait mode only, others in landscape mode, and some should have supported both orientations. I had never done it before; all my previous apps were in portrait mode.
After quick googling, I’ve found the
SystemChrome class and this question on StackOverflow. The mixins from StackOverflow answer work great until you begin to use
Navigator. When you push new screens to the navigation stack, everything is fine, but when you pop, the orientation settings of the previous screen are not restored. There’s no lifecycle function like
viewWillAppear in Flutter, and that screen doesn’t get any updates from the engine. I could have used a
Future returned from
Navigator.push, but the code would become ugly. Keeping track of all places where I need to update orientation settings is a bad idea. I needed a simple and elegant solution with as little overhead as possible.
I started developing my solution by asking myself two questions: “How can I know when the new screen goes to the stack?” and “How can I know when the screen leave the stack?” The answer was simple: use
NavigatorObserver. You can add multiple observers to any
Navigator, including the one created by
Let’s begin with writing the function which sets the screen orientation:
To make this function simpler to use, I defined an
enum with possible orientation options. You can add more options if needed. You may also want to add
ScreenOrientation.portraitOnly if your app is targeted to tablets too. Another reason to use your own
enum is to add an abstraction level. If Flutter gets another way to handle screen orientations, you only need to change the
The next step is to create a reusable
I’m using the
arguments field of the
RouteSettings class to store screen orientation settings. Don’t worry; you still can pass arguments to routes. If there are no arguments, or the type of
arguments field is not
ScreenOrientation, functions use the default option. In this example, it’s portrait-only, but you may change it.
Okay, now everything is ready to build the app:
I don’t like hardcoded values, so I always define constants for app routes.
rotationSettings is a little convenience function, it simplifies creating a
RouteSettings object with
arguments field set to rotation option. You may use the
arguments field of the settings object passed to
_onGenerateRoute to configure your widgets. I prefer to do it this way, instead of using
ModalRoute.of(context).settings.arguments in the widget. However, if you’re not like me, you still can use my solution, but you will need to adapt code a bit:
- Create a generic class for route arguments, which will have two fields: screen orientation and a generic field for route arguments.
didPushmethods to check for the object of this class in the
argumentsfield and then use the screen orientation field for
I’m not including the code of
RotatingScreen, because there’s no code related to the screen orientation. Everything is localized in
_onGenerateRoute function. You can find the full demo app in GitHub repository.
My solution is not perfect because the screen rotation starts at the same time as route transition, which may lead to some undesired effects, but it’s good enough and worked for my app. I hope it will help some of you too. If you found a better way to implement a per-screen orientation control in Flutter, I’ll be happy to know it!