【Flutter】シンプルなheaderとfooterを作成する

ProviderパターンでModelを作成してheaderとfooterのあるアプリを作成します
完成形は以下のような形

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492847/ce7cddff-8430-0869-f594-78e28c0f5a07.gif" width="50%">

（gifなのでぼやぼやですが、実際は高画質です）

footer(`BottomNavigationBar`)を押した場所によって、headerの文字とbodyに表示されるものを変えています。

# ファイル構造

今回はmodelを使います。

```
lib
├── header.dart
├── main.dart
├── models
│   └── bottom_navigation_model.dart
└── routes
    ├── home.dart
    ├── profile.dart
    └── settings.dart
```

# lib/main.dart

参考記事なんかを読んでいると`BottomNavigationBarItem`の`label`の部分を`title`にして`Text()`で描画している記事が多いですが、それはv1.19.0からは推奨されていないようです。

> @Deprecated('Use "label" instead, as it allows for an improved text-scaling experience. ' 'This feature was deprecated after v1.19.0.'), final

参考：[BottomNavigationBarItem class - widgets library - Dart API](https://api.flutter.dev/flutter/widgets/BottomNavigationBarItem-class.html)


---



```lib/main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'models/bottom_navigation_model.dart';
import 'routes/profile.dart';
import 'routes/home.dart';
import 'routes/settings.dart';

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

class MyApp extends StatelessWidget {
  final List _pageList = [
    Profile(),
    Home(),
    Settings(),
  ];
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutterの練習', //アプリの名前
      theme: ThemeData(
        primaryColor: Colors.green,
      ),
      home: ChangeNotifierProvider<BottomNavigationModel>(
        create: (_) => BottomNavigationModel(),
        child:
            Consumer<BottomNavigationModel>(builder: (context, model, child) {
          return Scaffold(
            // appBar: Header(), //各ファイルでHeader()を実行するのでいらない
            body: _pageList[model.currentIndex], //中身を描画
            //footer部分
            bottomNavigationBar: BottomNavigationBar(
              type: BottomNavigationBarType.fixed,
              items: [
                BottomNavigationBarItem(
                  icon: Icon(Icons.person),
                  label: 'プロフィール',
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.home),
                  label: 'ホーム',
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.settings),
                  label: '設定',
                ),
              ],
              currentIndex: model.currentIndex,
              onTap: (index) {
                model.currentIndex = index;
              },
              selectedItemColor: Colors.pinkAccent, //選んだ物の色
              unselectedItemColor: Colors.black45, //選んでない物の色
            ),
          );
        }),
      ),
    );
  }
}
```

# lib/header.dart

`Icons.add`を記述していますが、特に意味はありません。


```lib/header.dart
import 'package:flutter/material.dart';

class Header extends StatelessWidget with PreferredSizeWidget {
  final String headerTitle;
  Header({this.headerTitle}); //ヘッダータイトルを変更できるようにする
  @override
  Size get preferredSize => Size.fromHeight(kToolbarHeight);

  @override
  Widget build(BuildContext context) {
    return AppBar(
      title: Text(headerTitle),
      actions: [
        Padding(
          padding: const EdgeInsets.all(8.0),
          child: IconButton(
            icon: Icon(Icons.add),
            onPressed: () {
              //押したときの処理
            },
          ),
        )
      ],
    );
  }
}
```

# lib/models/bottom_navigation_model.dart

```lib/models/bottom_navigation_model.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class BottomNavigationModel extends ChangeNotifier {
  int _currentIndex = 1; //最初に表示される画面

  // getterとsetterを指定しています
  // setのときにnotifyListeners()を呼ぶことアイコンタップと同時に画面を更新しています。
  get currentIndex => _currentIndex;

  set currentIndex(int index) {
    _currentIndex = index;
    notifyListeners(); // View側に変更を通知
  }
}

```

# lib/routes/profile.dart

```lib/routes/profile.dart
import 'package:flutter/material.dart';
import '../header.dart';

class Profile extends StatelessWidget {
  final String screenName = 'プロフィール'; //headerに表示される名前
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: Header(headerTitle: screenName),
      body: Center(
        child: Text(screenName),
      ),
    );
  }
}
```


# lib/routes/home.dart

```lib/routes/home.dart
import 'package:flutter/material.dart';
import '../header.dart';

class Home extends StatelessWidget {
  final String screenName = 'ホーム'; //headerに表示される名前
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: Header(headerTitle: screenName),
      body: Center(
        child: Text(screenName),
      ),
    );
  }
}
```


# lib/routes/settings.dart

```lib/routes/settings.dart
import 'package:flutter/material.dart';
import '../header.dart';

class Settings extends StatelessWidget {
  final String screenName = '設定'; //headerに表示される名前
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: Header(headerTitle: screenName),
      body: Center(
        child: Text(screenName),
      ),
    );
  }
}
```

# 参考

[Flutter: シンプルでStatelessなBottomNavigationBarを作ってみた話](https://note.com/macgyverthink/n/n988be60b4a62)
[FlutterでTwitterクライアント作成②フッター追加](https://nekodeki.com/twitter_client%E2%91%A1%E3%83%95%E3%83%83%E3%82%BF%E3%83%BC%E8%BF%BD%E5%8A%A0/)