44

The API documentation: https://api.flutter.dev/flutter/material/Scaffold-class.html says:

The Scaffold was designed to be the single top level container for a MaterialApp and it's typically not necessary to nest scaffolds. For example in a tabbed UI, where the bottomNavigationBar is a TabBar and the body is a TabBarView, you might be tempted to make each tab bar view a scaffold with a differently titled AppBar. It would be better to add a listener to the TabController that updates the AppBar.

Does it mean there needs to be only one single Scaffold under the Material App or one single parent Scaffold for each page. If it's the first, how do we navigate? If the it's later, doesn't it mean the common AppBar and BottomBar get re-rendered on each navigation? What's the best practice.

dora
  • 1,314
  • 2
  • 22
  • 38
  • 4
    It's the latter. You will usually have 1 Scaffold per page, and yes that means the AppBar is re-rendered, but that's not an issue. – Ovidiu Mar 06 '20 at 10:21

1 Answers1

54

It means that, usually, there should be one Scaffold for each page (more precisely, for each Route/screen), and that you should not nest Scaffolds.

Navigating between screens

For example, take a look at this snippet that you can run in DartPad:

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    title: 'Navigation Basics',
    home: FirstRoute(),
  ));
}

class FirstRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Route'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Open route'),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => SecondRoute()),
            );
          },
        ),
      ),
    );
  }
}

class SecondRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Route"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: Text('Go back!'),
        ),
      ),
    );
  }
}

Here, you can see there are two distinct pages/Routes/screens, and each one has its own Scaffold. We are navigating back and forth by using Navigator, and so our pages are being added to the stack, one Scaffold on top of the other. That is fine.

If the it's later, doesn't it mean the common AppBar and BottomBar get re-rendered on each navigation?

Yes, but that is precisely what we want when we make two separate screens, each one with its own Scaffold.

Navigating inside the body of a Scaffold / nested navigations

On the other hand, take a look at this example:

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    title: 'Navigation Basics',
    home: FirstRoute(),
  ));
}

class FirstRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Route'),
      ),
      body: Scaffold(
        body: Center(
          child: RaisedButton(
            child: Text('Open route'),
            onPressed: () {},
          ),
        ),
      ),
    );
  }
}

Here, we are nesting two Scaffolds, and, as you can see, the second app bar is being drawn below the first app bar. That would not be the best approach for tabbed or nested navigations. If you want to navigate inside the body of a Scaffold, and change the app bar depending on the content, using TabControllers, such as DefaultTabController, is preferred. Take a look at this example:

import 'package:flutter/material.dart';

void main() {
  runApp(TabBarDemo());
}

class TabBarDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: DefaultTabController(
        length: 3,
        child: Scaffold(
          appBar: AppBar(
            bottom: TabBar(
              tabs: [
                Tab(icon: Icon(Icons.directions_car)),
                Tab(icon: Icon(Icons.directions_transit)),
                Tab(icon: Icon(Icons.directions_bike)),
              ],
            ),
            title: Text('Tabs Demo'),
          ),
          body: TabBarView(
            children: [
              Icon(Icons.directions_car),
              Icon(Icons.directions_transit),
              Icon(Icons.directions_bike),
            ],
          ),
        ),
      ),
    );
  }
}

As you can see, we have used only one Scaffold, since we are dealing with only one screen, really. It just happens that we want to show content pages and navigate inside the body of the Scaffold.

Conclusion

As a general rule of thumb: use only one Scaffold per Route/screen. Use only one Scaffold with widgets such as TabController or IndexedStack to navigate the content inside the body of a single screen.

drogel
  • 2,567
  • 2
  • 13
  • 22
  • 18
    What to do in case of the bottom navigation bar. I have a class where I am returning scaffold and for it's body I am passing array of pages where each page also contains it's own scaffold. Is it ok? It's working but I am not sure whether it's the right way – Dhananjay Gavali May 04 '21 at 13:20
  • 1
    Are no `keys` needed? Trying to learn best practices for usage with keys, when I noticed there is no mention or use of keys on this page. – Keith DC Jun 02 '21 at 22:03
  • @DhananjayGavali did you find the way? – Hardik Jun 20 '22 at 04:34
  • @Hardik actually bottom navigation bar in flutter mobile app seems quite complicated. The only problem with the navigation bar is routing. When we have nested pages we need to manage the routes properly otherwise it would be very hard to manage the navigation bar. We can use package for routing or package for bottom navbar too. that seems better solution – Dhananjay Gavali Jun 20 '22 at 20:42
  • 1
    Doesn't work for Drawer navigation. Feels as if Flutter is incomplete. – Oliver Dixon Sep 29 '22 at 18:50
  • In my case I just need FAB in only one page of bottomNav pages, in that case am I good to use `Scaffold` inside the existing bottomNav `Scaffold` – Shanu May 10 '23 at 09:13