Skip to content

Instantly share code, notes, and snippets.

@gabbygreat
Created March 5, 2025 21:48
Show Gist options
  • Select an option

  • Save gabbygreat/d978066ebfaaf9725f0a7cb588f32b6d to your computer and use it in GitHub Desktop.

Select an option

Save gabbygreat/d978066ebfaaf9725f0a7cb588f32b6d to your computer and use it in GitHub Desktop.
Tabbar Renderbox
import 'package:flutter/material.dart';
class TabModel {
String name;
String emoji;
List<String> content;
TabModel({
required this.content,
required this.name,
required this.emoji,
});
}
void main() {
runApp(
MaterialApp(
theme: ThemeData(
useMaterial3: false,
),
home: const CustomTabSwipeControl(),
),
);
}
class CustomTabSwipeControl extends StatefulWidget {
const CustomTabSwipeControl({super.key});
@override
State<CustomTabSwipeControl> createState() => _CustomTabSwipeControlState();
}
class _CustomTabSwipeControlState extends State<CustomTabSwipeControl>
with SingleTickerProviderStateMixin {
late TabController tabController;
late List<TabModel> items;
late ScrollController _scrollController;
final Map<String, GlobalKey> _headerKeys = {};
List<String> groceryItems = [
"Milk",
"Bread",
"Eggs",
"Rice",
"Pasta",
"Tomatoes",
"Potatoes",
"Chicken",
"Bananas",
"Cheese",
];
List<String> farmTools = [
"Hoe",
"Shovel",
"Rake",
"Tractor",
"Plow",
"Wheelbarrow",
"Pitchfork",
"Pruning Shears",
"Watering Can",
"Sickle",
];
List<String> gymEquipment = [
"Dumbbells",
"Treadmill",
"Kettlebells",
"Resistance Bands",
"Jump Rope",
"Medicine Ball",
"Pull-up Bar",
"Weight Bench",
"Rowing Machine",
"Exercise Mat",
];
List<String> officeSupplies = [
"Notebook",
"Pen",
"Stapler",
"Paper Clips",
"Printer",
"Desk Lamp",
"Whiteboard",
"USB Flash Drive",
"Calculator",
"File Cabinet",
];
@override
void initState() {
super.initState();
_scrollController = ScrollController();
items = [
TabModel(name: 'Grocery', content: groceryItems, emoji: 'πŸ›’'),
TabModel(name: 'Farm tools', content: farmTools, emoji: 'πŸ‘¨πŸΌβ€πŸŒΎ'),
TabModel(name: 'Gym', content: gymEquipment, emoji: 'πŸ‹πŸΌβ€β™‚οΈ'),
TabModel(name: 'Office', content: officeSupplies, emoji: 'πŸ’Ό'),
];
// Initialize keys for each section
for (var item in items) {
_headerKeys[item.name] = GlobalKey();
}
tabController = TabController(length: items.length, vsync: this);
_scrollController.addListener(_handleScroll);
}
void _handleScroll() {
for (int i = 0; i < items.length; i++) {
final item = items[i];
final key = _headerKeys[item.name];
if (key != null) {
final context = key.currentContext;
if (context != null) {
final renderBox = context.findRenderObject() as RenderBox?;
if (renderBox == null) continue;
// Get the scrollable container's render box
final scrollableContext =
_scrollController.position.context.storageContext;
final scrollableBox =
scrollableContext.findRenderObject() as RenderBox?;
if (scrollableBox == null) continue;
// Convert the header position relative to the scrollable container
final position = renderBox.localToGlobal(
Offset.zero,
ancestor: scrollableBox,
);
final double widgetTop = position.dy;
const double containerHeight = 200;
// If the header is within the top portion of the scrollable area
if (widgetTop >= 0 && widgetTop < containerHeight * 0.7) {
if (tabController.index != i) {
tabController.animateTo(i);
}
break;
}
}
}
}
}
void _onTabTapped(int index) {
tabController.index = index;
final key = _headerKeys[items[index].name];
if (key != null) {
Scrollable.ensureVisible(
key.currentContext!,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
}
@override
void dispose() {
tabController.dispose();
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.black,
elevation: 0,
bottom: TabBar(
controller: tabController,
isScrollable: true,
tabAlignment: TabAlignment.start,
onTap: _onTabTapped,
labelStyle: const TextStyle(
fontSize: 25,
),
tabs: items.map((e) => Tab(text: e.emoji)).toList(),
),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Expanded(
child: ColoredBox(
color: Colors.black,
child: Center(),
),
),
Container(
color: Colors.white,
height: 200,
child: Align(
alignment: Alignment.centerLeft,
child: SingleChildScrollView(
controller: _scrollController,
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 10,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: items.map((e) {
return Column(
key: _headerKeys[e.name],
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
e.name,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
const SizedBox(height: 5),
...e.content.map(
(item) => Text(item),
),
const SizedBox(height: 10),
],
);
}).toList(),
),
),
),
),
const Expanded(
child: ColoredBox(
color: Colors.black,
child: Center(),
),
),
],
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment