cyclic_tab_bar 1.0.3 copy "cyclic_tab_bar: ^1.0.3" to clipboard
cyclic_tab_bar: ^1.0.3 copied to clipboard

A Flutter package for cyclic tab bar component with infinite scrolling and full accessibility support.

📜 cyclic_tab_bar #

pub package

A Flutter package for a cyclic tab bar with infinite scrolling, providing separated tab bar and tab bar view components similar to Flutter's official TabBar API.

CyclicTabBar demo

✨ Features #

  • Separated Components: Use CyclicTabBar and CyclicTabBarView independently for flexible layouts
  • Custom Decorations: Wrap the tab bar with any decoration (gradients, shadows, etc.)
  • Infinite Scrolling: Seamlessly scroll through tabs and pages with wraparound support
  • Controller-based: Coordinate multiple components with CyclicTabController
  • Programmatic Control: Navigate to any tab index programmatically
  • Customizable: Extensive styling options for tabs, indicators, and animations

✍️ Usage #

Basic Example #

import 'package:cyclic_tab_bar/cyclic_tab_bar.dart';

DefaultCyclicTabController(
  contentLength: 10,
  child: Column(
    children: [
      CyclicTabBar(
        tabBuilder: (index, isSelected) => Text(
          'Tab $index',
          style: TextStyle(
            color: isSelected ? Colors.pink : Colors.black54,
            fontWeight: FontWeight.bold,
          ),
        ),
      ),
      Expanded(
        child: CyclicTabBarView(
          pageBuilder: (context, index, isSelected) => Center(
            child: Text('Page $index'),
          ),
        ),
      ),
    ],
  ),
)

For very large tab counts, consider enabling forceFixedTabWidth to avoid expensive per-tab measurement.

Custom Tab Bar Decoration #

One of the key benefits of the separated architecture is the ability to wrap the tab bar with custom decorations:

DefaultCyclicTabController(
  contentLength: 10,
  child: Column(
    children: [
      // Wrap tab bar with custom decoration
      Container(
        decoration: BoxDecoration(
          gradient: LinearGradient(
            colors: [Colors.blue.shade100, Colors.purple.shade100],
          ),
          boxShadow: [
            BoxShadow(
              color: Colors.black.withValues(alpha: 0.1),
              blurRadius: 4,
              offset: Offset(0, 2),
            ),
          ],
        ),
        child: CyclicTabBar(
          tabBuilder: (index, isSelected) => Text('Tab $index'),
          indicatorColor: Colors.pink,
          bottomBorder: BorderSide(color: Colors.black12, width: 2.0),
        ),
      ),
      Expanded(
        child: CyclicTabBarView(
          pageBuilder: (context, index, isSelected) => Center(
            child: Text('Page $index'),
          ),
          onPageChanged: (index) => print('Page changed to $index'),
        ),
      ),
    ],
  ),
)

Using Explicit Controller #

For more control, create your own CyclicTabController:

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> with SingleTickerProviderStateMixin {
  late CyclicTabController _controller;

  @override
  void initState() {
    super.initState();
    _controller = CyclicTabController(
      contentLength: 10,
      initialIndex: 5,
      vsync: this,
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        CyclicTabBar(
          controller: _controller,
          tabBuilder: (index, isSelected) => Text('Tab $index'),
        ),
        Expanded(
          child: CyclicTabBarView(
            controller: _controller,
            pageBuilder: (context, index, isSelected) => Center(
              child: Text('Page $index'),
            ),
          ),
        ),
        // Programmatic control
        ElevatedButton(
          onPressed: () => _controller.animateToIndex(7),
          child: Text('Go to Tab 7'),
        ),
      ],
    );
  }
}

Controller utilities

CyclicTabController exposes ergonomic helpers for runtime reconfiguration:

  • setIndex(index, {bool animated = true}) jumps or animates to a new tab without needing to tap.
  • setContentLength(newLength, {int? selectedIndex, bool animated = false}) updates the total tab/page count and optionally the current selection; CyclicTabBar and CyclicTabBarView automatically rebuild to reflect the change.
  • enableHapticFeedback can be set to false to disable selection haptics.

These complement the lower-level animateToIndex and jumpToIndex methods shown above.

Customization Options #

CyclicTabBar

  • tabBuilder: Builder function for tab widgets (can return any widget)
  • controller: Optional CyclicTabController
  • onTabTap: Callback when a tab is tapped
  • onTabLongPress: Callback when a tab is long pressed
  • indicatorColor: Color of the selection indicator
  • indicatorHeight: Height of the indicator
  • tabHeight: Height of the tab bar
  • tabPadding: Horizontal padding for each tab
  • forceFixedTabWidth: Whether to use fixed width tabs
  • fixedTabWidthFraction: Fraction of screen width for fixed tabs
  • bottomBorder: Border separator between tabs and content
  • backgroundColor: Background color of the tab bar

CyclicTabBarView

  • pageBuilder: Builder function for page widgets
  • controller: Optional CyclicTabController
  • onPageChanged: Callback when the page changes
  • scrollPhysics: Scroll physics for the page view

DefaultCyclicTabController

  • contentLength: Number of tabs/pages
  • initialIndex: Initial selected index (default: 0)
  • animationDuration: Duration of tab switching animations
  • enableHapticFeedback: Enable/disable selection haptics (default: true)
  • child: The widget tree that will use the controller

🔧 Migration from Old API #

If you were using the old combined CyclicTabBar widget (which included both tabs and pages), you can easily migrate:

Old API:

CyclicTabBar(
  contentLength: 10,
  tabBuilder: (index, isSelected) => Text('Tab $index'),
  pageBuilder: (context, index, isSelected) => Text('Page $index'),
)

New API:

DefaultCyclicTabController(
  contentLength: 10,
  child: Column(
    children: [
      CyclicTabBar(
        tabBuilder: (index, isSelected) => Text('Tab $index'),
      ),
      Expanded(
        child: CyclicTabBarView(
          pageBuilder: (context, index, isSelected) => Text('Page $index'),
        ),
      ),
    ],
  ),
)

💭 Have a question? #

If you have a question or found an issue, feel free to create an issue.

0
likes
150
points
26
downloads

Documentation

API reference

Publisher

verified publisherjha.sh

Weekly Downloads

A Flutter package for cyclic tab bar component with infinite scrolling and full accessibility support.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, meta

More

Packages that depend on cyclic_tab_bar