In Flutter, we can scroll to the top after tapping the status bar by passing the PrimaryScrollController
from the root Scaffold
to the ScrollView
. However, my app has a bottom navigation with 4 tabs, each containing a ScrollView. When I tap the status bar, all 4 tabs scroll to the top, but I only want the currently selected tab to scroll.
Following this thread, it seems like there’s no official way to do it
https://github.com/flutter/flutter/issues/85603#issuecomment-876798161
Are there any workarounds or alternative ways to scroll only the selected tab to the top after tapping the status bar?
1
I encountered the same problem a while ago and came up with my own solution. It may not be exactly what you’re looking for, but it worked perfectly for me.
I use only a list of offsets
, which I store the offset of each page being showed
Here is the example app code for you to test if you want:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _currentIndex = 0;
final ScrollController scrollController = ScrollController();
List<double> offsets = [];
@override
void initState() {
// Number of options in the bottomNavigationBar
offsets.addAll([0, 0, 0]);
super.initState();
}
final List<Widget> _pages = [
SingleChildScrollView(
child: Column(
children: List.generate(
20, (index) => ListTile(title: Text('Home Item $index'))),
),
),
SingleChildScrollView(
child: Column(
children: List.generate(
20, (index) => ListTile(title: Text('Search Item $index'))),
),
),
SingleChildScrollView(
child: Column(
children: List.generate(
20, (index) => ListTile(title: Text('Profile Item $index'))),
),
),
];
void _scrollToTop() {
scrollController.jumpTo(0);
}
void _onTabTapped(int index) {
// Select the offset for the selected page
offsets[_currentIndex] = scrollController.offset;
setState(() {
_currentIndex = index;
scrollController.jumpTo(offsets[index]);
});
}
@override
Widget build(BuildContext context) {
return PrimaryScrollController(
controller: scrollController,
child: Scaffold(
appBar: AppBar(
title: Text('BottomNavigationBar + Scroll'),
),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: _onTabTapped,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Search',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
),
],
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: _scrollToTop,
child: Icon(Icons.arrow_upward),
tooltip: 'Scroll to Top',
),
],
),
),
);
}
}