📱 Screen orientation
Common mistake
A common mistake is locking the application to portrait mode using
SystemChrome.setPreferredOrientations. This forces users who have mounted their device in landscape (for example, wheelchair users with a fixed mount) to use the app in an orientation that doesn’t work for them.
🎯 Relevant elements
This guideline applies to the application as a whole and any screen that explicitly restricts orientation:
- App-level orientation locks (
SystemChrome.setPreferredOrientations)- Individual screens or routes that override orientation
WCAG Guideline
This guideline is based on WCAG 2.2 — 1.3.4 Orientation (Level AA). Content must not restrict its view and operation to a single display orientation unless a specific orientation is essential.
Solution
Avoid calling SystemChrome.setPreferredOrientations unless the orientation is truly essential to the functionality (e.g. a video player in fullscreen). Where possible, support both portrait and landscape.
// ❌ Don't - locks the app to portrait, ignoring user's device orientation
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
// ✅ Do - support all orientations (this is the default; no call needed)
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
If a specific screen genuinely requires a fixed orientation, restore the full set when leaving that screen:
@override
void initState() {
super.initState();
SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft]);
}
@override
void dispose() {
SystemChrome.setPreferredOrientations(DeviceOrientation.values);
super.dispose();
}
Validation & Testing
Manual device testing
Rotate a physical device or emulator to both portrait and landscape while navigating through the app. Verify that no screen unexpectedly locks or resets to a fixed orientation.
flutter_test
testWidgets('screen does not restrict orientation', (tester) async {
await tester.pumpWidget(MyApp());
// Verify no SystemChrome orientation lock is applied by checking
// that the widget tree renders correctly in both orientations.
await tester.binding.setSurfaceSize(const Size(800, 400)); // landscape
await tester.pump();
expect(find.byType(MyHomePage), findsOneWidget);
});