Toggle full screen mode in Flutter by controlling the status and button bars

Toggle full screen mode in Flutter by controlling the status and button bars

·

6 min read

Introduction

In this tutorial, we will show how we can enter, as well exit, a "full screen mode" in a Flutter application. This can be achieved by controlling the visibility of the native platform's overlays. In the case of mobile platforms such as Android and iOS, the overlays are the status bar at the top, and optionally the button or navigation bar at the bottom.

This solution does not require using any external packages, so this should be simple to do. Let's get started!

Control system overlay visibility

As already mentioned, if we want our Flutter app to take the full screen, we need to hide any overlays by the native platform (system).

We can do this by using the static setEnabledSystemUIMode method provided by SystemChrome.

import 'package:flutter/services.dart'; // For `SystemChrome`

void enterFullScreen() {
    SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
}

void exitFullScreen() {
    SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: SystemUiOverlay.values);
}

That's it, actually! By setting the SystemUiMode.manual mode, we can specify which overlays should be visible. By passing an empty list, we can hide all overlays, and by providing all overlays, we can show them once again.

There are currently just two types of system UI overlays. One is the top overlay, typically the status bar, and the second is the bottom overlay, usually a button bar used for navigation. If we wanted, we could only hide just the top overlay, leaving the bottom button bar available for user interactions.

void enterFullScreenButKeepBottomOverlay() {
    SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.bottom]);
}

Types of full-screen modes

In the above example, we used the SystemUiMode.manual mode to achieve a full screen mode by hiding all overlays. But there are different values for SystemUiMode we could be using instead. All 4 modes will hide all overlays, but behave differently.

Manual with no overlays

This is the approach we used in the above example. All overlays are hidden, and the overlays can only be visible again by programmatically showing them (e.g. using the exitFullScreen() method above). Swiping the edges of the display can briefly show the overlays, but they are automatically hidden after a couple of seconds.

Lean back

In SystemUiMode.leanBack mode, while the overlays are initially hidden, they are visible after tapping anywhere on the display. One thing I noticed, for Android 12 at least, is that this only shows the navigation bar at the bottom (if there is one) and not the status bar, so this could depend on the operating system or device. You can still show the status bar by swiping the edges of the display.

Immersive

SystemUiMode.immersive mode is similar to leanBack mode, but the overlays are only shown when swiping the edges of the display.

Immersive sticky

SystemUiMode.immersiveSticky is the same as immersive, but with one difference. The swipe gesture which triggers showing the overlays can be received by the application, in case you want to react on it.

Edge to edge

In SystemUiMode.edgeToEdge mode, the overlays are still visible, but are actually rendered over the application. I tried this in an Android emulator, and the Flutter app's AppBar is shown below the status bar; it is not rendered on top of it. But the app's layout does seem to be affected when using this mode. This doesn't really achieve a full screen mode, but it's still good to know!

When enabling a system UI mode other than manual, we don't have to provide any overlays.

void enterFullScreen() {
    SystemChrome.setEnabledSystemUIMode(SystemUiMode.leanBack);
}

Listen to overlay changes

We can use SystemChrome.setSystemUIChangeCallback to register a callback to be called when system overlay visibility is changed. This could be useful, as in some modes the user can tap or swipe on the display, which could result to the overlays becoming visible again.

The callback has a systemOverlaysAreVisible boolean, and if its value is true, it means we are not in full screen mode. I noticed the value is not always what's expected though, so if you're planning on using it, make sure to test it out and see how it behaves.

To see it in action, we could register the callback in initState in a stateful widget.

@override
void initState() {
  super.initState();
  SystemChrome.setSystemUIChangeCallback((systemOverlaysAreVisible) async {
    log('System overlays are visible: $systemOverlaysAreVisible');
  });
}

Usage examples

Now that we know how to toggle full screen mode in a Flutter app, let's apply it in practice. We will use the default counter app/template to get started.

flutter create .

How our app utilizes full screen mode can depend on the use case. We might want to have "focus mode" which opens up in its own page, or we might want to toggle full screen mode on an existing page.

Enter a new page in full-screen mode

In the counter app, let's introduce a new focus mode page, which can be opened by a button on the main screen. The logic is identical to the main counter screen, but without the app bar.

// main.dart (_MyHomePageState)
TextButton(
    onPressed: () {
    Navigator.push(context,
        MaterialPageRoute(builder: (_) => const FocusModePage()));
    },
    child: const Text('Enter focus mode'),
),
// focus_mode.dart
class FocusModePage extends StatefulWidget {
  const FocusModePage({super.key});

  @override
  State<FocusModePage> createState() => _FocusModePageState();
}

class _FocusModePageState extends State<FocusModePage> {
  int _counter = 0;

  @override
  void initState() {
    super.initState();
    log('Entering full screen mode...');
    SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
  }

  @override
  void dispose() {
    super.dispose();
    log('Exiting full screen mode...');
    SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
        overlays: SystemUiOverlay.values);
  }

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

In this stateful widget, we enter full screen mode in initState, and exit it in dispose. This way, full screen mode is only enabled while the user is on this specific page.

Toggle full-screen mode in a page

If we want to enable full screen mode in an existing page, we could toggle it with buttons. On the main counter page, let's add two new buttons, conditional on whether full screen is enabled.

We add a variable named _isFullScreen to keep track of whether we enabled full screen mode or not.

// main.dart (_MyHomePageState)
bool _isFullScreen = false;

Depending on the value of _isFullScreen, we either show the button to enter full screen mode, or exit it.

// main.dart (_MyHomePageState)
if (!_isFullScreen)
  TextButton(
    onPressed: () {
      log('Entering full screen mode...');
      SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
      setState(() {
        _isFullScreen = true;
      });
    },
    child: const Text('Enter full screen'),
  ),
if (_isFullScreen)
  TextButton(
    onPressed: () {
      log('Exiting full screen mode...');
      SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
          overlays: SystemUiOverlay.values);
      setState(() {
        _isFullScreen = false;
      });
    },
    child: const Text('Exit full screen'),
  ),

This might become a bit more complicated if the gestures by a user cause the overlays to become visible as from the widget/state's point of view full screen is still enabled. For that, we could make use of SystemChrome.setSystemUIChangeCallback to track any changes.

Wrapping up

In this tutorial, we showed how to enter and exit full screen mode in a Flutter app without using any external packages. We also showed how we would use this functionality by either having certain full screen pages or enabling this mode manually on a page.

You can find the full source code here.

If you found this helpful and would like to be notified of future tutorials, please subscribe to the newsletter with your email below!

Did you find this article valuable?

Support Christos by becoming a sponsor. Any amount is appreciated!