pub

Flutter widget inspired by the classic Desktop-style tab component. Supports customizable themes.

classiccut darkcut mobilecut minimalistcut fromthescratchcut

The TabbedView renders the presentation of the model.

The TabbedViewModel stores the tab data as name, content, buttons or any dynamic value.

Get started

The TabbedViewTheme.classic() method builds the default theme.

    List<TabData> tabs = [];
    for (var i = 1; i < 7; i++) {
      Widget tabContent = Center(child: Text('Content $i'));
      tabs.add(TabData(text: 'Tab $i', content: tabContent));
    }
    TabbedWiew tabbedView = TabbedWiew(controller: TabbedWiewController(tabs));

classic

Content builder

It allows creating the contents of the tab dynamically during the selection event.

    var tabs = [
      TabData(text: 'Tab 1'),
      TabData(text: 'Tab 2'),
      TabData(text: 'Tab 3')
    ];

    TabbedWiew tabbedView = TabbedWiew(
        controller: TabbedWiewController(tabs),
        contentBuilder: (BuildContext context, int tabIndex) {
          int i = tabIndex + 1;
          return Center(child: Text('Content $i'));
        });

Close button tooltip

    var tabs = [
      TabData(text: 'Tab 1'),
      TabData(text: 'Tab 2'),
      TabData(text: 'Tab 3')
    ];
    TabbedWiew tabbedView = TabbedWiew(
        controller: TabbedWiewController(tabs),
        closeButtonTooltip: 'Click here to close the tab');

Tab

Extra button

    TabData tab = TabData(text: 'Tab', buttons: [
      TabButton(icon: Icons.star, onPressed: () => print('Hello!'))
    ]);
    TabbedWiew tabbedView = TabbedWiew(controller: TabbedWiewController([tab]));

tabbutton

Overriding theme color

    var tabs = [
      TabData(text: 'Tab 1'),
      TabData(text: 'Tab 2', buttons: [
        TabButton(
            icon: Icons.star,
            color: Colors.green,
            onPressed: () => print('Hello!'))
      ])
    ];
    TabbedWiew tabbedView = TabbedWiew(controller: TabbedWiewController(tabs));

tabbuttoncolor

Removing the close button

    var tabs = [
      TabData(text: 'Tab'),
      TabData(text: 'Non-closable tab', closable: false)
    ];
    TabbedWiew tabbedView = TabbedWiew(controller: TabbedWiewController(tabs));

nonclosabletab

Close listener

    bool _onTabClosing(int tabIndex) {
      if (tabIndex == 0) {
        print('The tab $tabIndex is busy and cannot be closed.');
        return false;
      }
      print('Closing tab $tabIndex...');
      return true;
    }

    List<TabData> tabs = [
      TabData(text: 'Tab 1'),
      TabData(text: 'Tab 2'),
      TabData(text: 'Tab 3')
    ];
    TabbedWiew tabbedView = TabbedWiew(
        controller: TabbedWiewController(tabs), onTabClosing: _onTabClosing);

Selection listener

    _onTabSelection(int? newTabIndex) {
      print('The new selected tab is $newTabIndex.');
    }

    List<TabData> tabs = [
      TabData(text: 'Tab 1'),
      TabData(text: 'Tab 2'),
      TabData(text: 'Tab 3')
    ];
    TabbedWiew tabbedView = TabbedWiew(
        controller: TabbedWiewController(tabs),
        onTabSelection: _onTabSelection);

Tabs area

Buttons

    TabbedWiewController controller = TabbedWiewController([]);

    TabbedWiew tabbedView = TabbedWiew(
        controller: controller,
        tabsAreaButtonsBuilder: (context, tabsCount) {
          List<TabButton> buttons = [];
          buttons.add(TabButton(
              icon: Icons.add,
              onPressed: () {
                int millisecond = DateTime.now().millisecondsSinceEpoch;
                controller.addTab(TabData(text: '$millisecond'));
              }));
          if (tabsCount > 0) {
            buttons.add(TabButton(
                icon: Icons.delete,
                onPressed: () {
                  if (controller.selectedIndex != null) {
                    controller.removeTab(controller.selectedIndex!);
                  }
                }));
          }
          return buttons;
        });

tabsareabuttons

Themes

Tab

Text style

    var tabs = [
      TabData(text: 'Tab 1'),
      TabData(text: 'Tab 2'),
    ];

    TabbedViewTheme theme = TabbedViewTheme.classic();
    theme.tabsArea.tab.textStyle = TextStyle(fontSize: 20, color: Colors.blue);

    TabbedWiew tabbedView =
        TabbedWiew(controller: TabbedWiewController(tabs), theme: theme);

tabtextstyle

Alignment

    var tabs = [
      TabData(text: 'Tab 1'),
      TabData(text: 'Tab 2'),
    ];

    TabbedViewTheme theme = TabbedViewTheme.classic();
    theme.tabsArea.tab
      ..textStyle = TextStyle(fontSize: 20)
      ..verticalAlignment = VerticalAlignment.top;

    TabbedWiew tabbedView =
        TabbedWiew(controller: TabbedWiewController(tabs), theme: theme);

topalignment

Tabs area

Color

  • The default TabsAreaTheme color is null.
    var tabs = [TabData(text: 'Tab 1'), TabData(text: 'Tab 2')];

    TabbedViewTheme theme = TabbedViewTheme.minimalist();
    theme.tabsArea.color = Colors.green[100];

    TabbedWiew tabbedView =
        TabbedWiew(controller: TabbedWiewController(tabs), theme: theme);

tabsareacolor

Tab gaps

  • Gap before the tabs (allows negative value).
  • Gap between tabs (allows negative value).
  • Minimum gap after tabs. Separates the last tab and the buttons area.
    List<TabData> tabs = [];
    for (var i = 1; i < 7; i++) {
      tabs.add(
          TabData(text: 'Tab $i', content: Center(child: Text('Content $i'))));
    }

    TabbedViewTheme theme = TabbedViewTheme.classic();
    theme.tabsArea
      ..initialGap = 20
      ..middleGap = 5
      ..minimalFinalGap = 5;

    TabbedWiew tabbedView =
        TabbedWiew(controller: TabbedWiewController(tabs), theme: theme);

customgap

Buttons area

Button icon for the hidden tabs menu
    List<TabData> tabs = [];
    for (var i = 1; i < 7; i++) {
      tabs.add(TabData(text: 'Tab $i'));
    }

    TabbedViewTheme theme = TabbedViewTheme.classic();
    theme.tabsArea.buttonsArea.hiddenTabsMenuButtonIcon =
        Icons.arrow_drop_down_circle_outlined;

    TabbedWiew tabbedView =
        TabbedWiew(controller: TabbedWiewController(tabs), theme: theme);

hiddentabsbuttonicon

Max width

    List<TabData> tabs = [];
    for (int i = 1; i < 11; i++) {
      tabs.add(TabData(text: 'Tab $i'));
    }

    TabbedViewTheme theme = TabbedViewTheme.classic()..menu.maxWidth = 100;

    TabbedWiew tabbedView =
        TabbedWiew(controller: TabbedWiewController(tabs), theme: theme);

menumaxwidth

Ellipsis on overflow text

    var tabs = [
      TabData(text: 'Tab 1'),
      TabData(text: 'Tab 2'),
      TabData(text: 'Tab 3'),
      TabData(
          text: 'The name of the tab is so long that it doesn'
              't fit on the menu')
    ];

    TabbedViewTheme theme = TabbedViewTheme.classic()
      ..menu.ellipsisOverflowText = true;

    TabbedWiew tabbedView =
        TabbedWiew(controller: TabbedWiewController(tabs), theme: theme);

menuellipsis

Predefined themes

Classic theme

    List<TabData> tabs = [];
    for (var i = 1; i < 7; i++) {
      tabs.add(TabData(text: 'Tab $i', content:  Center(child: Text('Content $i'))));
    }
    TabbedWiewController controller = TabbedWiewController(tabs);

    TabbedWiew tabbedView = TabbedWiew(
        controller: controller,
        theme: TabbedViewTheme.classic());

classic2

Color set
    List<TabData> tabs = [];
    for (var i = 1; i < 7; i++) {
      tabs.add(TabData(text: 'Tab $i', content:  Center(child: Text('Content $i'))));
    }
    TabbedWiewController controller = TabbedWiewController(tabs);

    TabbedViewTheme theme = TabbedViewTheme.classic(colorSet: Colors.green);

    TabbedWiew tabbedView = TabbedWiew(
        controller: controller,
        theme: theme);

classiccolorset

Dark theme

    List<TabData> tabs = [];
    for (var i = 1; i < 7; i++) {
      tabs.add(TabData(text: 'Tab $i'));
    }
    TabbedWiewController controller = TabbedWiewController(tabs);

    var contentBuilder = (BuildContext context, int index) {
      int i = index + 1;
      Text text = Text('Content $i', style: TextStyle(color: Colors.white));
      return Center(child: text);
    };

    TabbedWiew tabbedView = TabbedWiew(
        controller: controller,
        contentBuilder: contentBuilder,
        theme: TabbedViewTheme.dark());

    Container container = Container(child: tabbedView, color: Colors.black);

dark

Color set
    List<TabData> tabs = [];
    for (var i = 1; i < 7; i++) {
      tabs.add(TabData(text: 'Tab $i'));
    }
    TabbedWiewController controller = TabbedWiewController(tabs);

    var contentBuilder = (BuildContext context, int index) {
      int i = index + 1;
      Text text = Text('Content $i', style: TextStyle(color: Colors.white));
      return Center(child: text);
    };

    TabbedViewTheme theme = TabbedViewTheme.dark(colorSet: Colors.indigo);

    TabbedWiew tabbedView = TabbedWiew(
        controller: controller, contentBuilder: contentBuilder, theme: theme);

    Container container = Container(child: tabbedView, color: Colors.black12);

darkcolorset

Mobile theme

    TabbedWiew tabbedView =
        TabbedWiew(controller: controller, theme: TabbedViewTheme.mobile());

mobile

Color set
    TabbedViewTheme theme = TabbedViewTheme.mobile(colorSet: Colors.blueGrey);
    TabbedWiew tabbedView = TabbedWiew(controller: controller, theme: theme);

mobilecolorset

Highlighted tab color
    TabbedViewTheme theme =
        TabbedViewTheme.mobile(highlightedTabColor: Colors.green[700]!);
    TabbedWiew tabbedView = TabbedWiew(controller: controller, theme: theme);

mobilehighlightedcolor

Minimalist theme

    TabbedWiew tabbedView =
        TabbedWiew(controller: controller, theme: TabbedViewTheme.minimalist());

minimalist

Color set
    TabbedWiew tabbedView = TabbedWiew(
        controller: controller,
        theme: TabbedViewTheme.minimalist(colorSet: Colors.blue));

minimalistchangecolor

Creating new theme

It is possible to create an entire theme from scratch.

    var tabs = [
      TabData(text: 'Tab 1'),
      TabData(text: 'Tab 2'),
      TabData(text: 'Tab 3')
    ];

    TabbedViewTheme theme = TabbedViewTheme();
    theme.tabsArea
      ..border = Border(bottom: BorderSide(color: Colors.green[700]!, width: 3))
      ..middleGap = 6;

    Radius radius = Radius.circular(10.0);
    BorderRadiusGeometry? borderRadius =
        BorderRadius.only(topLeft: radius, topRight: radius);

    theme.tabsArea.tab
      ..padding = EdgeInsets.fromLTRB(10, 4, 10, 4)
      ..buttonsOffset = 8
      ..decoration = BoxDecoration(
          shape: BoxShape.rectangle,
          color: Colors.green[100],
          borderRadius: borderRadius)
      ..selectedStatus.decoration =
          BoxDecoration(color: Colors.green[200], borderRadius: borderRadius)
      ..highlightedStatus.decoration =
          BoxDecoration(color: Colors.green[50], borderRadius: borderRadius);

    TabbedWiew tabbedView =
        TabbedWiew(controller: TabbedWiewController(tabs), theme: theme);

fromthescratchcut

Agenda for the next few days

  • Complete documentation and examples to cover all available features.
  • Release the final version (1.0.0). The API may have some small changes.

Libraries

tabbed_view