見出し画像

FlutterでBottomNavigationBarのアイテムを押した時にスクロールを一番上に戻す

twitterなど、よくあるBottomNavigationを押したときにスクロールをトップに戻す実装について紹介します。


完成物


実装方法は簡単です。

1、同じ要素のButtomNavigationを検知する
2、スクロールリスナーを設定する
3、スクロールリスナー経由でスクロールを一番上に戻す

BottomNavigationの実装については下記をご覧ください

BottonNavigationの状態を残したい場合は下記の方法が有効だと思います

https://www.shogogeek.com/entry/20190221/1550745000


1、同じ要素のButtomNavigationを検知する

BottomNavigationのタッチイベントで検知します

void _selectedTab(int index) {
   if (_currentTab == index) {
     // ここにスクロールを戻す処理を書く
   } else {
     setState(() {
       _currentTab = index;
     });
   }
 }

2、スクロールリスナーを設定する

まずはScrollControllerのインスタンスを作成

final ScrollController _homeController = ScrollController();

作成したらスクロールするWidgetにControllerを設定します。
この例では CustomScrollViewにControllerを設定していますが、ListViewや、SingleChildScrollViewでも同じです。

body: CustomScrollView(
       controller: widget._controller,
       slivers: <Widget>[
         SliverStaggeredGrid.countBuilder(
           crossAxisCount: 2,
           itemBuilder: (context, index) {
             String name = index.toString();
             return _itemWidget(index, "assets/$name.png");
           },
           staggeredTileBuilder: (int index) => const StaggeredTile.fit(1),
           itemCount: 6,
         ),
       ],
     ),


3、スクロールリスナー経由でスクロールを一番上に戻す

次にスクロールを一番を一番上に戻す処理をかけば完成です。

void _selectedTab(int index) {
   if (_currentTab == index) {
       _homeController.animateTo(
         0.0,
         curve: Curves.easeOut,
         duration: const Duration(milliseconds: 300),
       );
     setState(() {
       _currentTab = index;
     });
   }
 }


補足

BottomNavigationItemの状態によって別のクラスに分けている場合は、親のWidgetでScrollControllerを宣言して子のWidgetに渡すのがいいでしょう。


class Home extends StatefulWidget {
 @override
 _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {

 int _currentTab = 0;

 final ScrollController _homeController = ScrollController();

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     body: Stack(
       children: <Widget>[
         _buildOffstage(0, Feed(_homeController)), ← 渡す
         _buildOffstage(1, MyPage(_myPageController)),  ← 渡す
       ],
     ),
     bottomNavigationBar: FABBottomAppBar(
       color: Colors.grey,
       selectedColor: Colors.black,
       onTabSelected: _selectedTab,
       items: [
         FABBottomAppBarItem(iconData: Icons.home, text: ""),
         FABBottomAppBarItem(iconData: Icons.person_outline, text: ""),
       ],
     )
   );
 }

 Widget _buildOffstage(int index, Widget page) {
   return Offstage(
     offstage: index != _currentTab,
     child: new TickerMode(
       enabled: index == _currentTab,
       child: page,
     ),
   );
 }

 void _selectedTab(int index) {
   if (_currentTab == index) {
     if (index == 0) {
       _homeController.animateTo(
         0.0,
         curve: Curves.easeOut,
         duration: const Duration(milliseconds: 300),
       );
     } else {
       _myPageController.animateTo(
         0.0,
         curve: Curves.easeOut,
         duration: const Duration(milliseconds: 300),
       );
     }

   } else {
     setState(() {
       _currentTab = index;
     });
   }
 }
}

投げ銭はいりません。それより無料でできる拡散をしてください!! 感想をツイートしていただけることが一番嬉しいです!!