I have 4 tabs in a flutter tab bar. I am using a silver App bar and the tab bar is inside the bottom property of the silver app bar. I have 4 containers of different sizes inside SingleChilScrollView. With click on each tab the screen scrolls to the specific container.
I have 3 issues:
- the first one is when I click on the tab it scroll container a bit more than what I want. I want to display the container top(with text in it)on top of the screen.
- the second issue(and the most important) is when I click on the tab it scroll the screen to a specific container but the tab active colour is misbehaving(sometimes it shows the active tab and other time it doesn’t).
- when I resize the web screen size then the tab is not scrolling to the accurate container location.
let’s have a situation, when a user click on the 3rd tab, the expected result will be the tab will be active by showing a blue line and screen will scroll to the 3rd container. the gained result is the screen is scrolled to the 3rd container but the tab active colour(that’s blue) is not working properly.
the expected result is:
expected result
the gained result is:
gained result
you can run the code is below:
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class ScrollContainerPage extends StatefulWidget {
@override
State<ScrollContainerPage> createState() => _ScrollContainerPageState();
}
class _ScrollContainerPageState extends State<ScrollContainerPage> {
ScrollController _scrollController = ScrollController();
List<GlobalKey> _globalKey = List.empty(growable: true);
late final secondContainerPosition;
late final thirdContainerPosition;
late final fourthContainerPosition;
bool initilized = false;
@override
void initState() {
for (int i = 0; i < 4; i++) {
_globalKey.add(GlobalKey());
}
super.initState();
}
getPosition() {
RenderBox box2 =
_globalKey[1].currentContext!.findRenderObject()! as RenderBox;
Offset position2 = box2.localToGlobal(Offset.zero);
if (!initilized) {
secondContainerPosition = position2.dy;
}
RenderBox box3 =
_globalKey[2].currentContext!.findRenderObject()! as RenderBox;
Offset position3 = box3.localToGlobal(Offset.zero);
if (!initilized) {
thirdContainerPosition = position3.dy;
}
RenderBox box4 =
_globalKey[3].currentContext!.findRenderObject()! as RenderBox;
Offset position4 = box4.localToGlobal(Offset.zero);
if (!initilized) {
fourthContainerPosition = position4.dy;
}
initilized = true;
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: DefaultTabController(
length: 4,
child: NestedScrollView(
floatHeaderSlivers: false,
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
return [
SliverAppBar(
leadingWidth: 200,
centerTitle: true,
//titleSpacing: 0,
//expandedHeight: 200.0,
backgroundColor: Colors.white,
leading: const Icon(Icons.arrow_back_ios,color: Colors.black,),
title: !kIsWeb? const Text("About us",
style: TextStyle(
color: Colors.black,
fontSize: 16.0,
),
):
SizedBox(
height: 40,
width: MediaQuery.of(context).size.width*0.5,
child: Center(
child: TextField(
cursorColor: const Color.fromRGBO(0, 79, 224, 1),
//maxLines: 5,
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(horizontal: 20),
prefixIcon: const Icon(Icons.search),
prefixIconColor: Colors.red,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20),
borderSide: const BorderSide(color: Color.fromRGBO(118, 118, 128, 1), width: 2),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(20),
borderSide: const BorderSide(color: Color.fromRGBO(118, 118, 128, 1), width: 1.5),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(20),
borderSide: const BorderSide(color: Color.fromRGBO(0, 79, 224, 1), width: 1.5),
),
),
),
),
),
actions: kIsWeb?[
Container(
margin: const EdgeInsets.fromLTRB(12,12,80,12),
padding: const EdgeInsets.symmetric(vertical: 5,horizontal: 30),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
border: Border.all(color: const Color.fromRGBO(4, 80, 225, 1)),
),
child: InkWell(
onTap: (){
},
child: Row(
children: const [
Icon(Icons.person_outline,
color: Color.fromRGBO(4, 80, 225, 1),
),
SizedBox(width: 10,),
Text('Sign in',
style: TextStyle(
color: Color.fromRGBO(4, 80, 225, 1),
fontSize: 14.0,
// fontWeight: FontWeight.w600,
),
),
],
),
),
),
]:null,
floating: !kIsWeb?true: false,
pinned: true,
snap: !kIsWeb?true: false,
bottom: TabBar(
indicatorColor: const Color.fromRGBO(0, 79, 224, 1),
tabs: [
Tab(icon: GestureDetector(
onTap: (){
setState(() {
getPosition();
_scrollController.animateTo(
_scrollController.position.minScrollExtent,
duration: const Duration(milliseconds: 500),
curve: Curves.ease);
});
},
child: const Text('scroll to red container', style: TextStyle(color: Colors.black),)),),
Tab(icon: GestureDetector(
onTap: (){
setState(() {
getPosition();
_scrollController.animateTo(secondContainerPosition,
// !kIsWeb ? 1140 : 1000,
duration: const Duration(milliseconds: 500),
curve: Curves.ease);
});
},
child: const Text('scroll to yellow container', style: TextStyle(color: Colors.black),)),),
Tab(icon: GestureDetector(
onTap: (){
setState(() {
getPosition();
_scrollController.animateTo(thirdContainerPosition,
// !kIsWeb ? 3380 : 2000,
duration: const Duration(milliseconds: 500),
curve: Curves.ease);
});
},
child: const Text('scroll to pink container', style: TextStyle(color: Colors.black),)),),
Tab(icon: GestureDetector(
onTap: (){
setState(() {
getPosition();
_scrollController.animateTo(fourthContainerPosition,
// _scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 500),
curve: Curves.ease);
});
},
child: const Text('scroll to orange container', style: TextStyle(color: Colors.black),)),),
]
),
),
];
},
body:
SingleChildScrollView(
controller: _scrollController,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
key: _globalKey[0],
height: 1000,
color: Colors.red,
child: const Text('red container')
),
const SizedBox(
height: 30,
),
Container(
key: _globalKey[1],
height: 1700,
color: Colors.yellow,
child: Text('yellow Container')
),
const SizedBox(
height: 30,
),
Container(
key: _globalKey[2],
height: 3000,
color: Colors.pink,
child: Text('pink Container')
),
const SizedBox(
height: 30,
),
Container(
key: _globalKey[3],
height: 500,
color: Colors.orange,
child: Text('orange Container'),
),
const SizedBox(
height: 30,
),
],
),
),
),
),
);
}
}
2
Answers
here is the solution. I used a package named scrollable_list_tab_scroller:
Here I’ve resolved your issue, you added
GestureDetector
to all the individual tabs which hindered the tap area on the actualTabBar
, what you needed to do instead was to useonTap
method of theTabBar
itself :