In this article, we're going to look at all the different ways to lay out and navigate between different features in your Flutter apps.

Flutter's pretty awesome for building apps that work everywhere – phones, computers, you name it. And a big part of making these apps user-friendly is getting navigation right.


This article won’t cover navigation between screens. Instead, we will cover options for how to design your UI to account for navigation in some common cases. So, let’s start by discussing some of the factors involved in making such a decision.


Let’s take a look at a couple of options and discuss when they can be useful:

Tab Bar Navigation

This usually involves a number of tabs either on the bottom (common in iOS apps) or the top (more common in Android) of the screen that lets users switch between features easily. This pattern is best used when you have a small set of equally important features on the same screen that you want users to easily access. Bottom tab bar-based layouts are great for UX because the user’s thumb can easily reach the tabs.

Design principles

Example

In this example, we will be using a Cupertino-style tab bar. You can refer to the official Flutter documentation to know how to use the material tab bar.

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return CupertinoTabScaffold(
      tabBar: CupertinoTabBar(items: const [
        BottomNavigationBarItem(
          icon: Icon(CupertinoIcons.home),
        ),
        BottomNavigationBarItem(
          icon: Icon(CupertinoIcons.settings),
        ),
      ]),
      tabBuilder: (builderContext, position) {
        return CupertinoTabView(
          builder: (tabViewContext) {
            return Center(
              child: Text('Tab $position'),
            );
          },
        );
      },
    );
  }
}

This example would look like this:

Carousels

Carousels are horizontal scroll-based layouts where the user swipes to switch between views. These are often used for features such as onboarding, photo galleries, etc. Carousels can be full screen or a percentage height; typically each individual view of a carousel is full width, but you can have them be slightly smaller to make it evident that there is a next item that can be scrolled to.

iOS typically has paged carousels, meaning you always fully scroll to the next item, even if the user only scrolls partially. Generally, if the user does only partially scroll to the next item, you should automatically scroll such that the item is fully in view.

When you have a few items in the carousel (for example, in your onboarding flow), it is a good idea to also add an indicator with your carousel that informs users about how many items there are and has a way to show what position they are currently at. If you have many items (let’s say a photo gallery), including an indicator will just add clutter. In this case, it’s better to add some sort of action (arrows, for example) that lets users trigger a scroll to the next item and then disable that action visually to indicate that they have reached the end.

Carousels are best used when you want to lay out more than 2-3 items (at least 3) horizontally. You should use a tab bar instead of a full-screen carousel if you have 2-3 items that are independent features.

Design principles

Example

While you can create a carousel from scratch in Flutter, you can also use a library created by someone else to implement some of these design patterns. For example, at the time of writing, carousel_slider was one of the more popular libraries on pub.dev for carousels in Flutter.

Segmented Control

This pattern is something that is used mainly in iOS and other Apple frameworks. Segmented control involves separating your view into separate segments that the user can toggle between using some controls. This type of pattern is useful when you need to separate your layout, but the different segments all share the same context (for example, switching between missed and normal calls in the phone app).

While Flutter allows you to implement this in Android, it may not be a good idea if the rest of the app follows the normal material guidelines because it would feel out of place.

Common use cases of segment controls include filtering content, switching between different views for the same data (list and map views, for example), etc.

Design principles

Example

In this example, we use CupertinoSlidingSegmentedControl, but you can use SegmentedButton if you want a more material look for your app.

class _MyHomePageState extends State<MyHomePage> {
  String selectedIndex = '1';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Center(
          child: Column(
            children: [
              CupertinoSlidingSegmentedControl(
                groupValue: selectedIndex,
                children: const {
                  "1": Text("First"),
                  "2": Text("Second"),
                },
                onValueChanged: (value) {
                  if (value != null) {
                    setState(() {
                      selectedIndex = value;
                    });
                  }
                },
              )
            ],
          ),
        ),
      ),
    );
  }
}

This will render the following:

Stories

Stories is inspired by popular social media apps but have since become a popular design pattern. Stories are a popular way of showcasing content in a sequential and immersive way, useful for showing content such as tutorials, guides, product showcases, etc.

Design principles

Example

At the time of writing this article, story_view was one of the more popular libraries for adding stories like layout to Flutter apps.


There are plenty more options when it comes to design patterns for navigation UI; you can pick and choose or combine multiple ones to design really good-looking apps. The key to choosing between them is to maintain clarity and ease of use for your users while keeping the general usability of your app as intuitive as possible.