tab_overview 0.2.10
tab_overview: ^0.2.10 copied to clipboard
Tab switcher with live thumbnails: show the tabs your app keeps open in a grid, zoom one into a full page, swipe between pages, drag to reorder and swipe to close.
Flutter tab switcher example
This Flutter tab switcher example opens five numbered tabs as live thumbnails in a grid: tap one, and it zooms into its page. Swipe to the next page, or pull the page down to zoom back out. Drag a thumbnail to reorder the tabs, swipe it away or tap its close button to close it, and tap + to add a tab.1
See the code › Install tab_overview ›
Get the highlights. #
- Live thumbnails. Each thumbnail is the tab's own page, scaled down to its cell, so it shows what the page shows.
- Zoom in and out. A tap zooms a thumbnail into its page, and the page back out, with a hero animation within the same page. A pull down zooms a page out too.
- Swipe between pages. An open tab is one page of many: swipe sideways to the next tab.
- Drag to reorder. A long press lifts a thumbnail, and the others slide out of its way as it moves.
- Close and add. A swipe or the close button closes a tab, and + adds one, as the grid closes the gap or makes room.
Take a closer look. #
Three steps, all in one build method:
- The controller. A
TabOverviewControllerholds the tabs, numbered 1 to 5, and whether they show as a grid of thumbnails or as pages. - The tab switcher.
TabOverview.builderbuilds each tab's page withtabBuilder. The grid shows the same page, scaled down, as the tab's thumbnail, andpullToCollapselets a pull down zoom a page back out. - The changes. A tap on a thumbnail calls
expand, and a tap on a pagecollapse. The buttons calltoggleMode,removeandadd, and the tab switcher animates each change.
import 'dart:math';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:tab_overview/tab_overview.dart';
import 'style/style.dart';
import 'tab_page.dart';
void main() => runApp(
MaterialApp(
title: 'TabOverview Example',
debugShowCheckedModeBanner: false,
theme: exampleTheme,
home: const TabOverviewExample(),
),
);
/// Numbered tabs in a tab switcher, as live thumbnails in a grid, each of
/// which zooms into its page:
///
/// 1. A [TabOverviewController] holds the tabs, and whether they show as a
/// grid of thumbnails or as pages.
/// 2. [TabOverview.builder] builds each tab's page, which the grid shows,
/// scaled down, as its thumbnail.
/// 3. A tap zooms a thumbnail into its page, and the buttons switch between
/// the grid and the pages, close the active tab and add one.
class TabOverviewExample extends StatefulWidget {
const TabOverviewExample({super.key});
@override
State<TabOverviewExample> createState() => TabOverviewExampleState();
}
class TabOverviewExampleState extends State<TabOverviewExample> {
// 1. The controller, with tabs 1 to 5 open in the grid.
final controller = TabOverviewController<int>(initialTabs: [1, 2, 3, 4, 5]);
// The number of the last tab added, so each new tab has one of its own.
var _lastTab = 5;
@override
Widget build(BuildContext context) {
final padding = MediaQuery.viewPaddingOf(context);
return Scaffold(
backgroundColor: Theme.of(context).colorScheme.surfaceContainerHigh,
// 2. The tab switcher: the grid of thumbnails, or the tabs' pages.
body: TabOverview<int>.builder(
controller: controller,
tabBuilder: (context, tab) => TabPage(
tab,
// 3. A tap zooms a thumbnail into its page, and a page back out.
onTap: () => controller.isTabExpanded(tab)
? controller.collapse()
: controller.expand(tab),
),
removeTabButtonBuilder: (context, tab) =>
CloseTabButton(onPressed: () => controller.remove(tab)),
thumbnailsGridPadding: EdgeInsets.fromLTRB(
S.s4,
max(padding.top, S.s4),
S.s4,
max(padding.bottom, S.s4),
),
thumbnailDecoration: ShapeDecoration(shape: squircle(S.s7)),
// Pull a page down to zoom it back out.
pullToCollapse: true,
// Drag with the mouse too, to turn the page and to pull it down.
scrollBehavior: const MaterialScrollBehavior().copyWith(
dragDevices: PointerDeviceKind.values.toSet(),
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: TabButtons(
// 3. Switch between the grid and the pages.
onSwitchView: controller.toggleMode,
// 3. Close the active tab, if any is left.
onClose: () {
final tab = controller.activeTab;
if (tab != null) controller.remove(tab);
},
// 3. Add a tab.
onAdd: () => controller.add(++_lastTab),
),
);
}
}
Questions? Answers. #
How do I make a tab switcher in Flutter?
Give a TabOverviewController your tabs, and build their pages in TabOverview.builder, as the example above does. The tabs show as live thumbnails in a grid: expand zooms one into its page, and collapse, or a pull down with pullToCollapse, zooms it back out. A tab can be anything: a number, as here, or your own model of a document, a chat or a form.
Is this a TabBar?
No. Flutter's TabBar shows a row of tab labels, and its TabBarView swipes between their pages. tab_overview shows multiple tabs at once, each as a thumbnail of its page, and zooms the tapped one into a full page, where a swipe turns to the next tab. People add, close and reorder the tabs as they go.
How do I add and close tabs dynamically?
Call the controller's add with a new tab, or remove with the tab to close, as the example's buttons do. A tab's close button and a swipe to the left close it too, and isTabRemovable keeps a tab from being closed.
How do I reorder tabs with drag and drop?
There's nothing to add: a long press lifts a thumbnail, and a drag moves it, as the others slide out of its way. isTabReorderable keeps a tab in its place, and addTabsReorderedListener tells you when the order changes, which the controller's tabs then has.
What happens to a tab's state when it zooms?
The tab's page is built anew as it zooms in or out, so a State in it starts over. Keep what should carry over, such as a scroll offset or typed text, in your tab object, and give it to the page as it's built. Tabs added, closed or moved around a tab keep its state.
Can a thumbnail look different from its page?
Yes. tabThumbnailBuilder builds a tab's thumbnail, such as a card with the tab's title, in place of its scaled-down page.
1. Recorded from the example frame by frame, at 60 fps, in an iPhone 15 frame.