import 'package:flutter/material.dart';
class FilterTabs extends StatefulWidget {
final List<String> labels;
final int selected;
final ValueChanged<int> onSelected;
const FilterTabs({
super.key,
required this.labels,
required this.selected,
required this.onSelected,
});
@override
State<FilterTabs> createState() => _FilterTabsState();
}
class _FilterTabsState extends State<FilterTabs> {
// One key per tab, so we can later find and scroll to any of them.
late final List<GlobalKey> _keys = List.generate(
widget.labels.length,
(_) {
return GlobalKey();
},
);
@override
void initState() {
super.initState();
// Tabs aren't laid out yet, so wait one frame, then scroll to the
// tab that's selected when the strip first appears.
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollTo(widget.selected);
});
}
void _scrollTo(int index) {
final BuildContext? context = _keys[index].currentContext;
if (context == null) return;
// Bring the tapped tab fully into view, centered.
Scrollable.ensureVisible(
context,
alignment: 0.5,
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
);
}
@override
void didUpdateWidget(FilterTabs oldWidget) {
super.didUpdateWidget(oldWidget);
// If the selection changed (from a tap or from the parent), scroll to it.
if (oldWidget.selected != widget.selected) {
_scrollTo(widget.selected);
}
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: 44,
child: ListView.builder(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 16),
itemCount: widget.labels.length,
itemBuilder: (context, i) {
final String label = widget.labels[i];
final bool selected = i == widget.selected;
return _Tab(
// Attach this tab's key so we can scroll to it.
key: _keys[i],
label: label,
selected: selected,
onTap: () {
widget.onSelected(i);
},
);
},
),
);
}
}
class _Tab extends StatelessWidget {
final String label;
final bool selected;
final VoidCallback onTap;
const _Tab({
super.key,
required this.label,
required this.selected,
required this.onTap,
});
@override
Widget build(BuildContext context) {
final Color background = selected ? Colors.black : Colors.black12;
final Color foreground = selected ? Colors.white : Colors.black;
return Padding(
padding: const EdgeInsets.only(right: 8),
child: GestureDetector(
onTap: onTap,
behavior: HitTestBehavior.translucent,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
alignment: Alignment.center,
decoration: BoxDecoration(
color: background,
borderRadius: BorderRadius.circular(22),
),
child: Text(
label,
style: TextStyle(color: foreground),
),
),
),
);
}
}