ra_segmented_switch 1.0.0
ra_segmented_switch: ^1.0.0 copied to clipboard
A beautiful, customizable segmented switch widget for Flutter with smooth animations and extensive customization options.
RA Segmented Switch #
A highly customizable, animated segmented switch widget for Flutter applications. Built on top of Flutter's TabBar with a beautiful pill-style indicator, offering smooth animations, extensive theming options, and seamless integration with your app's design system.

Table of Contents #
- Features
- Installation
- Quick Start
- Usage Examples
- API Reference
- Customization Guide
- Migration Guide
- Contributing
- License
Features #
Core Features #
- Multiple Size Options: Three predefined sizes (small, medium, large) with customizable dimensions
- Flexible Icon Support: Icons on left, right, or center with full customization
- Smooth Animations: Built-in animations with customizable duration and easing curves
- Auto-Scrolling: Intelligent scrolling behavior when content exceeds available space
- Theme Integration: Seamless integration with Flutter's theme system and Material Design
Customization Features #
- Custom Colors: Full control over background, indicator, text, and shadow colors
- Border Radius: Adjustable corner radius from sharp to fully rounded
- Typography: Custom text styles for selected and unselected states
- Shadow Effects: Configurable shadow color and opacity
- Responsive Design: Adapts to different screen sizes and orientations
Developer Experience #
- Type Safety: Full Dart type safety with comprehensive documentation
- Performance Optimized: Efficient rendering with minimal rebuilds
- Accessibility Ready: Built-in accessibility support with proper semantics
- Easy Integration: Simple API that works with existing Flutter apps
- Robust Error Handling: Graceful handling of edge cases and invalid states
Installation #
Add ra_segmented_switch
to your pubspec.yaml
file:
dependencies:
ra_segmented_switch: ^0.0.1
Then run:
flutter pub get
Import the package in your Dart code:
import 'package:ra_segmented_switch/ra_segmented_switch.dart';
Quick Start #
Here's a simple example to get you started:
import 'package:flutter/material.dart';
import 'package:ra_segmented_switch/ra_segmented_switch.dart';
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: RASegmentedSwitch(
items: [
RASegmentedSwitchItem(
label: 'Home',
leftIcon: Icons.home,
),
RASegmentedSwitchItem(
label: 'Search',
leftIcon: Icons.search,
),
RASegmentedSwitchItem(
label: 'Profile',
leftIcon: Icons.person,
),
],
onTap: (index) {
print('Selected: $index');
},
),
),
),
);
}
}
Usage Examples #
Basic Usage #
Simple Text Segments
RASegmentedSwitch(
items: [
RASegmentedSwitchItem(label: 'Option 1'),
RASegmentedSwitchItem(label: 'Option 2'),
RASegmentedSwitchItem(label: 'Option 3'),
],
onTap: (index) {
print('Selected index: $index');
},
)
With Icons
RASegmentedSwitch(
items: [
RASegmentedSwitchItem(
label: 'Home',
leftIcon: Icons.home,
),
RASegmentedSwitchItem(
label: 'Favorites',
leftIcon: Icons.favorite,
),
RASegmentedSwitchItem(
label: 'Settings',
rightIcon: Icons.settings,
),
],
initialIndex: 0,
size: RASegmentedSwitchSize.medium,
onTap: (index) {
// Handle selection
},
)
Advanced Customization #
Theme Toggle Switch
class ThemeToggle extends StatefulWidget {
@override
_ThemeToggleState createState() => _ThemeToggleState();
}
class _ThemeToggleState extends State<ThemeToggle> {
int selectedIndex = 0;
@override
Widget build(BuildContext context) {
final isDark = selectedIndex == 0;
return RASegmentedSwitch(
items: [
RASegmentedSwitchItem(
label: 'Dark',
leftIcon: Icons.dark_mode,
),
RASegmentedSwitchItem(
label: 'Light',
leftIcon: Icons.light_mode,
),
],
initialIndex: selectedIndex,
backgroundColor: isDark
? const Color(0xFF2C2C2E)
: const Color(0xFFF2F2F7),
indicatorColor: isDark
? const Color(0xFF1C1C1E)
: Colors.white,
selectedLabelStyle: TextStyle(
color: isDark ? Colors.white : Colors.black,
fontWeight: FontWeight.w600,
fontSize: 14,
),
unselectedLabelStyle: TextStyle(
color: isDark ? Colors.grey[400] : Colors.grey[600],
fontWeight: FontWeight.w400,
fontSize: 14,
),
shadowColor: isDark
? Colors.black.withOpacity(0.3)
: Colors.grey.withOpacity(0.2),
borderRadius: 12,
size: RASegmentedSwitchSize.medium,
onTap: (index) {
setState(() {
selectedIndex = index;
});
// Apply theme change
},
);
}
}
Navigation Tabs
class NavigationExample extends StatefulWidget {
@override
_NavigationExampleState createState() => _NavigationExampleState();
}
class _NavigationExampleState extends State<NavigationExample> {
int currentIndex = 0;
final List<Widget> pages = [
HomePage(),
SearchPage(),
FavoritesPage(),
ProfilePage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Navigation Example'),
bottom: PreferredSize(
preferredSize: Size.fromHeight(60),
child: Padding(
padding: EdgeInsets.all(16),
child: RASegmentedSwitch(
items: [
RASegmentedSwitchItem(
label: 'Home',
leftIcon: Icons.home_outlined,
),
RASegmentedSwitchItem(
label: 'Search',
leftIcon: Icons.search_outlined,
),
RASegmentedSwitchItem(
label: 'Favorites',
leftIcon: Icons.favorite_outline,
),
RASegmentedSwitchItem(
label: 'Profile',
leftIcon: Icons.person_outline,
),
],
initialIndex: currentIndex,
size: RASegmentedSwitchSize.large,
backgroundColor: Theme.of(context).primaryColor.withOpacity(0.1),
indicatorColor: Theme.of(context).primaryColor,
selectedLabelStyle: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
onTap: (index) {
setState(() {
currentIndex = index;
});
},
),
),
),
),
body: IndexedStack(
index: currentIndex,
children: pages,
),
);
}
}
Scrollable Content
RASegmentedSwitch(
items: [
RASegmentedSwitchItem(label: 'All', leftIcon: Icons.all_inclusive),
RASegmentedSwitchItem(label: 'Documents', leftIcon: Icons.description),
RASegmentedSwitchItem(label: 'Images', leftIcon: Icons.image),
RASegmentedSwitchItem(label: 'Videos', leftIcon: Icons.videocam),
RASegmentedSwitchItem(label: 'Audio', leftIcon: Icons.audiotrack),
RASegmentedSwitchItem(label: 'Archives', leftIcon: Icons.archive),
RASegmentedSwitchItem(label: 'Others', leftIcon: Icons.more_horiz),
],
minScrollableItems: 4, // Scrollable when more than 4 items
size: RASegmentedSwitchSize.small,
borderRadius: 8,
onTap: (index) {
// Filter content based on selection
},
)
Custom Styling Examples #
Rounded Corners
RASegmentedSwitch(
items: [
RASegmentedSwitchItem(label: 'Rounded'),
RASegmentedSwitchItem(label: 'Corners'),
],
borderRadius: 25,
backgroundColor: Colors.blue[50],
indicatorColor: Colors.blue[600],
onTap: (index) => {},
)
Sharp Edges
RASegmentedSwitch(
items: [
RASegmentedSwitchItem(label: 'Sharp'),
RASegmentedSwitchItem(label: 'Edges'),
],
borderRadius: 4,
backgroundColor: Colors.grey[200],
indicatorColor: Colors.black87,
onTap: (index) => {},
)
Custom Colors
RASegmentedSwitch(
items: [
RASegmentedSwitchItem(label: 'Custom'),
RASegmentedSwitchItem(label: 'Colors'),
],
backgroundColor: Color(0xFFE3F2FD),
indicatorColor: Color(0xFF1976D2),
shadowColor: Color(0xFF1976D2).withOpacity(0.3),
selectedLabelStyle: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
unselectedLabelStyle: TextStyle(
color: Color(0xFF1976D2),
fontWeight: FontWeight.w500,
),
onTap: (index) => {},
)
API Reference #
RASegmentedSwitch #
The main widget class for creating segmented switches.
Constructor
RASegmentedSwitch({
Key? key,
required List<RASegmentedSwitchItem> items,
required ValueChanged<int> onTap,
int initialIndex = 0,
RASegmentedSwitchSize size = RASegmentedSwitchSize.medium,
TextStyle? selectedLabelStyle,
TextStyle? unselectedLabelStyle,
int? minScrollableItems,
double? borderRadius,
Color? backgroundColor,
Color? indicatorColor,
Color? shadowColor,
})
Properties
Property | Type | Default | Description |
---|---|---|---|
items |
List<RASegmentedSwitchItem> |
required | List of items to display in the segmented switch |
onTap |
ValueChanged<int> |
required | Callback function called when a segment is tapped |
initialIndex |
int |
0 |
Initial selected segment index |
size |
RASegmentedSwitchSize |
medium |
Size of the segmented switch |
selectedLabelStyle |
TextStyle? |
null |
Text style for the selected segment label |
unselectedLabelStyle |
TextStyle? |
null |
Text style for unselected segment labels |
minScrollableItems |
int? |
4 |
Minimum number of items before the switch becomes scrollable |
borderRadius |
double? |
999 |
Border radius of the container (999 = fully rounded) |
backgroundColor |
Color? |
Color(0xFFD5D9E4) |
Background color of the segmented switch container |
indicatorColor |
Color? |
Color(0xFFFDFCFF) |
Color of the selection indicator |
shadowColor |
Color? |
Colors.black |
Shadow color of the selection indicator |
RASegmentedSwitchItem #
Represents an individual segment in the switch.
Constructors
// Regular constructor with label and optional icons
RASegmentedSwitchItem({
required String label,
IconData? leftIcon,
IconData? rightIcon,
})
// Icon-only constructor (planned for future release)
RASegmentedSwitchItem.iconOnly({
required IconData icon,
})
Properties
Property | Type | Description |
---|---|---|
label |
String |
Text label displayed in the segment |
leftIcon |
IconData? |
Icon displayed to the left of the label |
rightIcon |
IconData? |
Icon displayed to the right of the label |
icon |
IconData? |
Icon displayed in the center (for icon-only segments) |
RASegmentedSwitchSize #
Enum defining the available sizes for the segmented switch.
enum RASegmentedSwitchSize {
small, // Height: 38px
medium, // Height: 48px
large, // Height: 56px
}
Size Specifications
Size | Height | Vertical Padding | Use Case |
---|---|---|---|
small |
38px | 4px | Compact interfaces, toolbars |
medium |
48px | 8px | Standard UI elements, forms |
large |
56px | 12px | Primary navigation, prominent controls |
Customization Guide #
Theming Integration #
The widget automatically adapts to your app's theme, but you can override specific aspects:
// Using theme colors
RASegmentedSwitch(
items: items,
backgroundColor: Theme.of(context).colorScheme.surfaceVariant,
indicatorColor: Theme.of(context).colorScheme.primary,
selectedLabelStyle: Theme.of(context).textTheme.labelMedium?.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
fontWeight: FontWeight.bold,
),
unselectedLabelStyle: Theme.of(context).textTheme.labelMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
onTap: (index) => {},
)
Responsive Design #
Make your segmented switch responsive to different screen sizes:
class ResponsiveSegmentedSwitch extends StatelessWidget {
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
return RASegmentedSwitch(
items: items,
size: screenWidth > 600
? RASegmentedSwitchSize.large
: RASegmentedSwitchSize.medium,
minScrollableItems: screenWidth > 800 ? 6 : 4,
onTap: (index) => {},
);
}
}
Animation Customization #
The widget uses an AnimatedContainer
with customizable animation properties:
- Duration: 200 milliseconds
- Curve:
Curves.easeInOut
These values are optimized for the best user experience but can be indirectly influenced through the widget's state changes.
Accessibility #
The widget includes built-in accessibility features:
- Semantic labels for screen readers
- Proper focus management
- Keyboard navigation support
- High contrast support
To enhance accessibility further:
RASegmentedSwitch(
items: [
RASegmentedSwitchItem(label: 'Home'), // Automatically gets semantic label
RASegmentedSwitchItem(label: 'Search'),
],
// Ensure sufficient color contrast
backgroundColor: Colors.white,
indicatorColor: Colors.blue[700],
selectedLabelStyle: TextStyle(
color: Colors.white,
fontSize: 16, // Minimum recommended size
),
onTap: (index) => {},
)
Performance Considerations #
Optimization Tips #
- Minimize Rebuilds: Use
const
constructors where possible - Efficient Callbacks: Keep
onTap
callbacks lightweight - Icon Caching: Use standard Material icons for better performance
- State Management: Consider using state management solutions for complex scenarios
Memory Usage #
The widget is designed to be memory-efficient:
- Minimal widget tree depth
- Efficient TabController management
- Automatic disposal of resources
Migration Guide #
From Version 0.0.1 #
This is the initial release, so no migration is needed.
Future Versions #
Migration guides will be provided for future releases with breaking changes.
Troubleshooting #
Common Issues #
TabController Length Mismatch
Error: "Controller's length property does not match the number of tabs"
Solution: This has been fixed in version 0.0.1 with proper didUpdateWidget
implementation.
Performance Issues with Many Items
Problem: Sluggish performance with many segments
Solution: The widget automatically becomes scrollable when there are more than the specified minScrollableItems
(default: 4).
Theme Not Applied
Problem: Custom theme colors not showing
Solution: Ensure you're passing the correct theme colors:
RASegmentedSwitch(
backgroundColor: Theme.of(context).colorScheme.surface,
indicatorColor: Theme.of(context).colorScheme.primary,
// ... other properties
)
Debug Mode #
Enable debug mode to see additional information:
import 'package:flutter/foundation.dart';
// In debug mode, the widget provides additional assertion checks
assert(() {
if (kDebugMode) {
print('RASegmentedSwitch: ${items.length} items loaded');
}
return true;
}());
Examples Repository #
For more comprehensive examples, check out the /example
directory in the repository, which includes:
- Basic usage examples
- Theme integration demos
- Navigation implementations
- Custom styling showcases
- Performance benchmarks
Contributing #
We welcome contributions! Please see our Contributing Guide for details.
Development Setup #
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
Reporting Issues #
Please report issues on our GitHub Issues page with:
- Flutter version
- Device/platform information
- Minimal reproduction code
- Expected vs actual behavior
Roadmap #
Upcoming Features #
- Icon-only segments
- Vertical orientation support
- Custom animation curves
- Gradient backgrounds
- Badge support
- Haptic feedback
Version History #
See CHANGELOG.md for detailed version history.
License #
This project is licensed under the MIT License - see the LICENSE file for details.
Support #
- Documentation: This README and inline code documentation
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Acknowledgments #
- Built with Flutter's powerful widget system
- Inspired by iOS UISegmentedControl and Material Design principles
- Thanks to the Flutter community for feedback and contributions
Made with ❤️ for the Flutter community