Tabs

In ProgressDark modeRTL

Tabbed content panels for organizing information.


7:21
Account
Password
Notifications
Name
Abdullah Yousif
Username
@ronakcn

Installation

Copy the component into your project using the CLI:

$ronakcn add tabs

Usage

Import and use the component in your Flutter widget tree:

lib/pages/example.dart
import 'package:flutter/material.dart';
import '../components/tabs/tabs.dart';

class ExamplePage extends StatelessWidget {
  const ExamplePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('RcnTabs Example')),
      body: Center(
        child: RcnTabs(
          // Configure props here
        ),
      ),
    );
  }
}

Variants

Tabs ships with the following variants:

default

Standard underline tab bar.

7:21
Account
Password
Notifications
Name
Abdullah Yousif
Username
@ronakcn

with-icon

Tabs with icons alongside labels.

7:21
Account
Password
Notifications
Name
Abdullah Yousif
Username
@ronakcn

scrollable

Horizontally scrollable tab bar.

7:21
Account
Password
Notifications
Name
Abdullah Yousif
Username
@ronakcn

Props

PropTypeDefaultDescription
tabs*List<TabItem>List of tab definitions.
activeIndexint0Currently active tab index.
onChanged*ValueChanged<int>Called when tab changes.

Source files

lib/components/ui/tabs.dart
import 'package:flutter/material.dart';

class TabItem {
  const TabItem({required this.label, required this.content, this.icon});
  final String label;
  final Widget content;
  final IconData? icon;
}

class RcnTabs extends StatefulWidget {
  const RcnTabs({super.key, required this.tabs, this.activeIndex = 0, required this.onChanged});
  final List<TabItem> tabs;
  final int activeIndex;
  final ValueChanged<int> onChanged;

  @override
  State<RcnTabs> createState() => _RcnTabsState();
}

class _RcnTabsState extends State<RcnTabs> {
  late int _active;
  @override
  void initState() { super.initState(); _active = widget.activeIndex; }

  @override
  Widget build(BuildContext context) {
    final colors = Theme.of(context).colorScheme;
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          children: List.generate(widget.tabs.length, (i) {
            final isActive = i == _active;
            return GestureDetector(
              onTap: () { setState(() => _active = i); widget.onChanged(i); },
              child: Padding(
                padding: const EdgeInsets.only(right: 16, bottom: 8),
                child: Column(
                  children: [
                    Text(widget.tabs[i].label,
                      style: TextStyle(fontSize: 14, fontWeight: isActive ? FontWeight.w600 : FontWeight.normal,
                        color: isActive ? colors.onSurface : colors.onSurface.withOpacity(0.5))),
                    const SizedBox(height: 4),
                    if (isActive) Container(height: 2, width: 40, color: colors.onSurface),
                  ],
                ),
              ),
            );
          }),
        ),
        widget.tabs[_active].content,
      ],
    );
  }
}
Version 0.1.0 · navigation category