Created
March 5, 2025 21:48
-
-
Save gabbygreat/d978066ebfaaf9725f0a7cb588f32b6d to your computer and use it in GitHub Desktop.
Tabbar Renderbox
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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