Flutterで水平方向にスクロールできるタブバーを実装する手順

公開日:2019/06/04 更新日:2019/06/04
Flutterで水平方向にスクロールできるタブバーを実装する手順のサムネイル
Photo by Thought Catalog on Unsplash

はじめに

ニュースアプリなどでアプリのトップバーにタブを表示してかつそのタブをスクロールできるように実装されているレイアウトを見ることがあると思います。例えばカテゴリ毎にタブが別れている場合などです。この記事では、水平方向にスクロールできるタブバーを実装する手順をまとめます。

できるようになること

以下のGIF画像のように、スクロール可能なタブバーを実装します。また、アクティブでないタブの文字を薄くして表示します。

scrollable-top-tab-options.gif

このデモのコードは以下のGitHubにあげてあります。

github.com

Scrollable tab sample

参考文献

以下の公式ドキュメントのコードを参考にさせて頂きました。

flutter.dev

Working with tabs is a common pattern in apps following the Material Designguidelines. Flutter includes a convenient way to create tab layouts as part ofthe [material library]({{site.api}}/flutter/material/material-library.html).## Directions 1. Create a `TabController` 2. Create the tabs 3. Create content for each tab## 1. Create a `TabController`In order for tabs to...

前提と環境

以下の通りです。

  • Flutter 1.5.4-hotfix.2
  • Dart 2.3.0

スクロールできるタブバーのシンプルな例

以下が冒頭に載せたGIF画像のコードになります。基本的には参考文献の公式ドキュメントのコードになりますが、isScrollableによって水平方向にスクロール可能にし、unselectedLabelColorでアクティブでないタブの文字色を半透明にしています。これらオプションの内容は後述します。

lib/main.dart
import 'package:flutter/material.dart';

void main() {
  runApp(TabBarDemo());
}

class TabBarDemo extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: DefaultTabController(
        // タブの数
        length: 7,
        child: Scaffold(
          appBar: AppBar(
            bottom: TabBar(
              // タブのオプション
              isScrollable: true,
              unselectedLabelColor: Colors.white.withOpacity(0.3),
              unselectedLabelStyle: TextStyle(fontSize: 12.0),
              labelColor: Colors.yellowAccent,
              labelStyle: TextStyle(fontSize: 16.0),
              indicatorColor: Colors.white,
              indicatorWeight: 2.0,
              // タブに表示する内容
              tabs: [
                      Tab(
                        child: Text('Top'),
                      ),
                      Tab(
                        child: Text('Business'),
                      ),
                      Tab(
                        child: Text('Technology'),
                      ),
                      Tab(
                        child: Text('Finance'),
                      ),
                      Tab(
                        child: Text('Food'),
                      ),
                      Tab(
                        child: Text('Economic'),
                      ),
                      Tab(
                        child: Text('Game'),
                      )
              ],
            ),
            title: Text('Tabs Demo'),
          ),
          body: TabBarView(
            // 各タブの内容
            children: [
              Icon(Icons.directions_car),
              Icon(Icons.directions_transit),
              Icon(Icons.directions_bike),
              Icon(Icons.directions_car),
              Icon(Icons.directions_transit),
              Icon(Icons.directions_bike),
              Icon(Icons.directions_car)
            ],
          ),
        ),
      ),
    );
  }
}

上記コードの中で使用しているタブバーの見た目に関わるオプションは以下になります。

オプション名 内容
isScrollable trueでタブバーの水平方向へのスクロールを可能にします。
unselectedLabelColor タブバーのアクティブでない(選択中でない)テキストの色を指定します。上記コードでは、アクティブでないタブバーの文字色を半透明の白色に設定するためにColors.white.withOpacity(0.3)を与えています。
unselectedLabelStyle タブバーのアクティブでない(選択中でない)テキストのスタイルを指定します。ここで指定できるスタイルは、TextStyle classに記載されています。
labelColor タブバーのアクティブである(選択中である)テキストの色を指定します。
labelStyle タブバーのアクティブである(選択中である)テキストのスタイルを指定します。ここで指定できるスタイルは、TextStyle classに記載されています。
indicatorColor タブバーのアクティブであるテキスト下に表示されるインディケーター(冒頭のGIFのデモの場合、白色のバー)の色を指定します。
indicatorWeight タブバーのアクティブであるテキスト下に表示されるインディケーターの太さを指定します。デフォルトでは2.0で大きくするほど太くなります。

その他の全てのオプションは以下の公式ドキュメントに記載されています。

api.flutter.dev

A material design widget that displays a horizontal row of tabs.

各タブの内容を別ファイルから読み込みたい場合

すでに載せたコードでは各タブの内容がアイコンしかない例でしたが、同じmain.dartに定義したウィジェットを読み込んだり、以下のように別ファイルからウィジェットを読み込むことももちろんできます。以下では、例えばプロジェクト名がtopbarmain.dartと同じディレクトリにfirsttab.dartというファイルがある前提です。

lib/main.dart
import 'package:topbar/firsttab.dart';
(...途中省略...)
         body: TabBarView(
            // 各タブの内容
            children: [
              // Icon(Icons.directions_car),
              new FirstTab('test'), // 別ファイルで定義しているウィジェットを使用する。
              Icon(Icons.directions_transit),
              Icon(Icons.directions_bike),
              Icon(Icons.directions_car),
              Icon(Icons.directions_transit),
              Icon(Icons.directions_bike),
              Icon(Icons.directions_car)
            ],
          ),
(...以下省略...) 

firsttab.dartは例えば以下のように定義します。以下ではcategoryNameという適当な文字列を親ウィジェットから受け取れるようにしていますが、もちろんなくてもOKです。

lib/firsttab.dart
import 'package:flutter/material.dart';

class FirstTab extends StatefulWidget {
  FirstTab(this.categoryName);
  final String categoryName;

  
  _FirstTabState createState() => _FirstTabState();
}

class _FirstTabState extends State {
  
  
  void initState() {
    super.initState();
  }

  
  Widget build(BuildContext context) {
    return new Scaffold(
      backgroundColor: Colors.blue,
      body: new Container(
        child: new Center(
          child: new Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              new Icon(
                Icons.library_books,
                size: 50.0,
                color: Colors.white,
              ),
              new Text(
                widget.categoryName,
                style: new TextStyle(color: Colors.white),
              )
            ],
          ),
        ),
      ),
    );
  }
}

まとめ

Flutterではアプリでよく見るような機能が大体デフォルトで揃っており便利です。

関連記事

開発アプリ

nanolog.app

毎日の小さな出来事をなんでも記録して、ログとして残すためのライフログアプリです。