tab_overview 0.2.6
tab_overview: ^0.2.6 copied to clipboard
A widget that enables smooth tab transitions with a grid view of thumbnails for easy tab management and navigation.
example/lib/main.dart
import 'dart:math';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:tab_overview/tab_overview.dart';
void main() {
runApp(const MainApp());
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) =>
const MaterialApp(home: TabOverviewExample());
}
class TabOverviewExample extends StatefulWidget {
const TabOverviewExample({super.key});
@override
State<TabOverviewExample> createState() => TabOverviewExampleState();
}
class TabOverviewExampleState extends State<TabOverviewExample> {
// Step 1: Initialize a TabOverviewController with your tab type.
// This controller will manage the state, including switching tabs, adding, and removing.
final controller = TabOverviewController<Tab>(
initialMode: TabOverviewMode.expanded,
);
// The number of the last tab added, so each new tab has a number of its own.
var _lastTabNum = 0;
@override
Widget build(BuildContext context) {
final viewPadding = MediaQuery.viewPaddingOf(context);
return Scaffold(
backgroundColor: Colors.grey.shade200,
// Step 2: Use TabOverview.builder to create the TabOverview widget.
// Configure the controller, padding, tab content builder, and styling for thumbnails.
body: TabOverview<Tab>.builder(
controller: controller, // Attach the controller
thumbnailsGridPadding: EdgeInsets.only(
top: max(viewPadding.top, 16.0),
right: 16.0,
bottom: max(viewPadding.bottom, 16.0),
left: 16.0,
), // Customize grid padding
tabBuilder: (context, tab) => TabWidget(
tab: tab,
// Step 3: Toggle tab expansion or collapse when a thumbnail is tapped.
onTap: () => controller.isTabExpanded(tab)
? controller
.collapse() // Collapse if expanded
: controller.expand(tab), // Expand on tap
),
thumbnailDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(
32.0,
), // Add rounded corners to thumbnails
),
// Pull the expanded tab down to collapse it.
pullToCollapse: true,
// Which tabs the user may close or move. Every tab may, by default.
isTabRemovable: (tab) => tab.removable,
isTabReorderable: (tab) => tab.reorderable,
// Drag with the mouse too, to turn the page and to pull the expanded
// tab down, as hero_here's examples do.
scrollBehavior: const MaterialScrollBehavior().copyWith(
dragDevices: PointerDeviceKind.values.toSet(),
),
),
floatingActionButton: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisSize: MainAxisSize.max,
children: [
// A button to toggle between overview and expanded modes.
IconButton(
onPressed: controller.toggleMode,
icon: const Icon(Icons.auto_awesome_motion_outlined),
),
// A button to close the active tab, if there's any tab left.
IconButton(
onPressed: () {
final tab = controller.activeTab;
if (tab != null) controller.remove(tab);
},
icon: const Icon(Icons.remove),
),
// A button to dynamically add new tabs.
IconButton(
onPressed: () => controller.add(Tab(++_lastTabNum)),
icon: const Icon(Icons.add),
),
],
),
),
);
}
}
class TabWidget extends StatelessWidget {
final Tab tab;
final VoidCallback? onTap;
const TabWidget({super.key, required this.tab, this.onTap});
@override
Widget build(BuildContext context) => GestureDetector(
onTap: onTap,
child: Container(
color: Colors.grey.shade300,
child: Center(
child: Text(
'$tab',
style: Theme.of(context).textTheme.bodyLarge
?.copyWith(fontWeight: FontWeight.bold, fontSize: 60),
),
),
),
);
}
// A Tab model. TabOverview's isTabRemovable and isTabReorderable read its
// `removable` and `reorderable`, which a tab could set to false to stay put.
class Tab {
final int num;
Tab(this.num);
bool get removable => true;
bool get reorderable => true;
@override
String toString() => '$num';
}