TabbedScaffold class

A Scaffold-like widget tailored for tab-bar navigation.

When creating a Scaffold that uses a BottomNavigationBar, it's not uncommon to end up with code like this:

final List<AppBar> _appBars = [
  AppBar(
    title: const Text('Tab 1'),
    actions: [...],
    ...
  ),
  AppBar(
    title: const Text('Tab 2'),
    actions: [...],
    ...
  ),
  ... // more AppBars
];

final List<Widget> _bodies = [
  TabBody1(),
  TabBody2(),
  ... // more Widgets
];

final List<BottomNavigationBarItem> _bottomNavigationBarItems = [
  BottomNavigationBarItem(
    title: const Text('Tab 1'),
    icon: Icon(Icons.tab1),
  ),
  BottomNavigationBarItem(
    title: const Text('Tab 2'),
    icon: Icon(Icons.tab2),
  ),
  ... // more BottomNavigationBarItems
];

int _currentIndex = 0;

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: _appBars[_currentIndex],
    body: _bodies[_currentIndex],
    ...
    bottomNavigationBar: BottomNavigationBar(
      items: _bottomNavigationBarItems,
      currentIndex: _currentIndex,
      onTap: onTabPressed,
    )
    ...
  );
}

void onTabPressed(int index) {
  setState(() {
    _currentIndex = index;
  });
}

In this pattern, UI elements are grouped by type - i.e. app bars, bodies, bottom navigation bar items, etc. This can be hard to understand and read because what makes up a single "tab" is spread across multiple data structures. Additionally, there is a good amount of boilerplate code:

  • the bare minimum onTap necessary to switch between tabs,
  • the repetition of tab titles in both the AppBar and BottomNavigationBarItem,
  • the manual use of _currentIndex to switch UI components between tabs
  • manual class instantiations for widgets (AppBar, BottomNavBarItem,)

TabbedScaffold aims to reorganize your code so that UI components are grouped by their tab, not their type, and reduce the amount of code you need to write for a tab-bar navigation layout.

The above code can be re-written in the TabbedScaffold pattern as follows:

List<ScaffoldTab> _tabs = [
  ScaffoldTab(
    title: const Text('Queue'),
    body: TabBody1()
    bottomNavigationBarItemIcon: Icon(Icons.tab1),
  ),
  ScaffoldTab(
    title: const Text('Activity'),
    body: Container(color: Colors.green,),
    bottomNavigationBarItemIcon: Icon(Icons.tab2),
  ),
  ... // more ScaffoldTabs
];

@override
Widget build(BuildContext context) {
  return TabbedScaffold(
    tabs: _tabs,
  );
}

TabbedScaffold takes a list of ScaffoldTab instances. Additionally, it optionally takes any properties normally passed to a Scaffold constructor (these can be thought of property templates).

When determining how to build the Scaffold for a specific tab, TabbedScaffold first looks at the specific properties in the ScaffoldTab instance for that tab. For any properties not defined in the ScaffoldTab, it looks for any templates passed to the TabbedScaffold and applies the properties found there. If no template is passed for a specific category, TabbedScaffold uses a bare instance of that property as a template.

This allows you to specify properties that should be the same across all tabs (for example, AppBar.leading = null) without having to specify as such in each ScaffoldTab.

Here is an example of using AppBar and BottomNavigationBar templates with TabbedScaffold :

TabbedScaffold(
  tabs: _tabs,
  appBar: AppBar( // Applies this to every tab. Gets overridden by any
                  // properties set in _tabs[i]
    leading: null,
    automaticallyImplyLeading: false,
  ),
  bottomNavigationBar: BottomNavigationBar(
    onTap: (index) => { // Do something },
  ),
)
Inheritance

Constructors

TabbedScaffold({@required List<ScaffoldTab> tabs, AppBar appBar, Widget floatingActionButton, FloatingActionButtonLocation floatingActionButtonLocation, FloatingActionButtonAnimator floatingActionButtonAnimator, List<Widget> persistentFooterButtons, Widget drawer, Widget endDrawer, Color backgroundColor, Widget bottomNavigationBar, Widget bottomSheet, bool resizeToAvoidBottomInset, bool primary, DragStartBehavior drawerDragStartBehavior, bool extendBody})

Properties

appBar AppBar
final
backgroundColor Color
final
bottomNavigationBar Widget
final
bottomSheet Widget
final
drawer Widget
final
drawerDragStartBehavior DragStartBehavior
final
endDrawer Widget
final
extendBody bool
final
floatingActionButton Widget
final
floatingActionButtonAnimator FloatingActionButtonAnimator
final
floatingActionButtonLocation FloatingActionButtonLocation
final
hashCode int
The hash code for this object. [...]
@nonVirtual, read-only, inherited
key Key
Controls how one widget replaces another widget in the tree. [...]
final, inherited
persistentFooterButtons List<Widget>
final
primary bool
final
resizeToAvoidBottomInset bool
final
runtimeType Type
A representation of the runtime type of the object.
read-only, inherited
tabs List<ScaffoldTab>
final

Methods

createElement() StatefulElement
Creates a StatefulElement to manage this widget's location in the tree. [...]
inherited
createState() → _TabbedScaffoldState
Creates the mutable state for this widget at a given location in the tree. [...]
override
debugDescribeChildren() List<DiagnosticsNode>
Returns a list of DiagnosticsNode objects describing this node's children. [...]
@protected, inherited
debugFillProperties(DiagnosticPropertiesBuilder properties) → void
Add additional properties associated with the node. [...]
inherited
noSuchMethod(Invocation invocation) → dynamic
Invoked when a non-existent method or property is accessed. [...]
inherited
toDiagnosticsNode({String name, DiagnosticsTreeStyle style}) DiagnosticsNode
Returns a debug representation of the object that is used by debugging tools and by DiagnosticsNode.toStringDeep. [...]
inherited
toString({DiagnosticLevel minLevel: DiagnosticLevel.info}) String
Returns a string representation of this object.
inherited
toStringDeep({String prefixLineOne: '', String prefixOtherLines, DiagnosticLevel minLevel: DiagnosticLevel.debug}) String
Returns a string representation of this node and its descendants. [...]
inherited
toStringShallow({String joiner: ', ', DiagnosticLevel minLevel: DiagnosticLevel.debug}) String
Returns a one-line detailed description of the object. [...]
inherited
toStringShort() String
A short, textual description of this widget.
inherited

Operators

operator ==(Object other) bool
The equality operator. [...]
@nonVirtual, inherited