タブ移動でアニメーションをつける方法【Flutter】

# 始めに
こんな感じでスワイプとタブのタップの両方でページを切り替えられるものを作ります

https://github.com/shotaro427/page_animation_sample
こちらに実際に動くコードがあるので、もし分かりずらかったりしたらこちらもご覧ください

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363545/259e46f8-61a5-027f-1d03-537e1d9c8a6d.gif" width=250 >

## 環境
Flutter: `1.20.0` 
channel: `stable`
Dart: `2.9.1`


# BottomNavigationBarを作る

https://api.flutter.dev/flutter/material/BottomNavigationBar-class.html

## UIを作成
まずは見た目を作成します

```dart:main.dart
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      //　~~~ 略 ~~~
      //　~~~以下追加~~~
      bottomNavigationBar: BottomNavigationBar(
        items: const [
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            title: Text('ホーム'),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.pages),
            title: Text('コンテンツ'),
          ),
        ],
      ),
    );
  }
```

これでホットリロードすると、こんな感じの見た目になるはずです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363545/5a271a7d-5f83-81e9-9695-e74372e4c040.png" width=300 >

けど、まだボタンを押しても何にも起こらないと思うので、まずは「コンテンツ」を押したらアクティブが変わるようにしましょう

## アイコンの動的化

```dart:main.dart
class _MyHomePageState extends State<MyHomePage> {
  // ~~~ 略 ~~~

  // ~~~ 以下追加 ~~~
  int _currentIndex = 0; 

  void onItemTapped(int index) {
    setState(() {
      _currentIndex = index;
    });
  }
  // ~~~ ここまで ~~~

  // ~~~ 略 ~~~
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // ~~~ 略 ~~~
      bottomNavigationBar: BottomNavigationBar(
        items: const [
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            title: Text('ホーム'),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.pages),
            title: Text('コンテンツ'),
          ),
        ],
        currentIndex: _currentIndex,  // 追加!!
        onTap: onItemTapped,          // 追加!!
      ),
    );
  }
}
```

これでタブバーのアイコンを選択すると色が変わるようになったと思います！
では、次はページを作ってタブを押すとページが切り替わる部分を作っていきます


# PageViewを作る

https://api.flutter.dev/flutter/widgets/PageView-class.html

PageViewを使うと、スワイプなどのアクションでアニメーション付きのページ切り替えを実装することができます

### 中身の作成

ページ切り替えをするコンテンツのWidgetを作ります
ここでは単純にWidgetの配列を定義するだけです

```dart:main.dart

class _MyHomePageState extends State<MyHomePage> {
  // ~~~ 略 ~~~

  // ~~~~ 以下追加 ~~~
  static const List<Widget> _widgets = <Widget>[
    Center(
      child: Text(
        'This Page is HOME',
        style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
      ),
    ),
    Center(
      child: Text(
        'This Page is Contents',
        style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
      ),
    ),
  ];
  // ~~~ ここまで ~~~

  // ~~~ 略 ~~~
}
```

次にScaffoldのBody部分にPageViewを実装していきます

```dart:main.dart


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // ~~~ 略 ~~~

      // ~~~ 以下変更 ~~~
      body: PageView(
        children: _widgets,
        onPageChanged: onItemTapped,
      ),
      // ~~~ ここまで ~~~
      bottomNavigationBar: BottomNavigationBar(
        items: const [
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            title: Text('ホーム'),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.pages),
            title: Text('コンテンツ'),
          ),
        ],
        currentIndex: _currentIndex,
        onTap: onItemTapped,
      ),
    );
  }
```

これでリロードをすると、スワイプでページが切り替わるようになったと思います
しかし、タブのアイコンでのページ切り替えをまだ作っていないので、次でその部分を作っていきます！



# PageControllerを作る

https://api.flutter.dev/flutter/widgets/PageController-class.html

先ほど変更したPageViewに自作のPageControllerを適用させて、タブをアイコンを押した時にページの切り替えを行うようにしていきましょう

```dart:main.dart
class _MyHomePageState extends State<MyHomePage> {
  // ~~~ 略 ~~~

  // ~~~ 追加 ~~~
  final PageController _pageController = PageController();
  // ~~~ ここまで ~~~

  void onItemTapped(int index) {
    // ~~~ 追加 ~~~
    _pageController.animateToPage(index,
        duration: const Duration(milliseconds: 100), curve: Curves.easeIn);
    // ~~~ ここまで ~~~
    setState(() {
      _currentIndex = index;
    });
  }

  // ~~~ 略 ~~~

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // ~~~ 略 ~~~
      body: PageView(
        children: _widgets,
        onPageChanged: onItemTapped,
        // ~~~~ 以下追加 ~~~
        controller: _pageController, 
      ),
      // ~~~ 略 ~~~
    );
  }
}

```

新しく`PageController`を定義して、それを先ほど作った`PageView`に適用。

そのコントローラーを通して、タブのアイコンがタップされた時にアニメーション付きでページ切り替えを行うという仕組みです


# 最後に
Flutterでアプリを作っていて、タブのページ遷移ってどうやるんだ？と思ったので色々調べてみました
もしお役に立ったなら幸いです！