Flutter 初心者が知りたい小技集 ② ナビゲーションの書き方

## はじめに

Flutter 初心者が知りたい小技集 の続きです。

小技集

> [Flutter 初心者が知りたい小技集 ① ウィジェットの分割](https://qiita.com/em0/items/6ade4e9721a9135f4dc8)
> [Flutter 初心者が知りたい小技集 ③ asyncとawaitを使ったhttp通信](https://qiita.com/em0/items/396851bcd213dff4422e)
> [Flutter 初心者が知りたい小技集 ④ SharedPreferencesでMapを扱う](https://qiita.com/em0/items/e04f3cf87240f5fb7496)

今回は Flutter のナビゲーションについてです。ナビゲーションの実装についてはいくつか方法があると思いますが、今回はその中でも個人的にわかりやすいと思った名前ベースのルーティングについて紹介します。

> [Navigate with named routes](https://flutter.dev/docs/cookbook/navigation/named-routes)

こちらの公式を参考に少し改良して実装していきます。

## ナビゲーションの実装

### プロジェクトを作成

`$ flutter create navigation_sample`

### lib/main.dart を編集

全て消してコピペしてください

【Code】

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'navigation sample',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('navigation sample'),
        ),
        body: Center(
          child: Text("LET'S START!"),
        ));
  }
}
```

<img width="281" alt="スクリーンショット 2020-07-30 15.31.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279625/b9dab19d-58f1-a4e2-0bdc-d2ce5cc0a62f.png">


### 画面を追加

参考に沿って一つ画面を追加します。ついでにホーム画面も分割しましょう

【Command Line】

```
$ mkdir lib/screens
$ touch lib/screens/second_screen.dart
$ touch lib/screens/my_home_page.dart
```

【Code】

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

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Screen"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            // Navigate back to first screen when tapped.
          },
          child: Text('Go back!'),
        ),
      ),
    );
  }
}
```

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

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('navigation sample'),
        ),
        body: Center(
          child: Text("LET'S START!"),
        ),);
  }
}
```

### ルートを定義

lib/main.dart を以下のように編集します。各画面をインポートしてルーティングを設定しています。ホームページの画面は分割したので削除しています。

【Code】

```
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:navigation_sample/screens/second_screen.dart';

// 追加
import './screens/my_home_page.dart';
import './screens/second_screen.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'navigation sample',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        // 追加
        initialRoute: '/',
        routes: {
          '/': (ctx) => MyHomePage(),
          '/second': (ctx) => SecondScreen(),
        });
  }
}
```

### 遷移する

遷移するためのボタンと処理を追加します。

【Code】

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

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('navigation sample'),
        ),
        body: Center(
          // 追加・変更
          child: FlatButton(
            child: Text("GO TO 2nd SCREEN"),
            onPressed: () {
              // 定義した名前に沿って遷移する
              Navigator.pushNamed(context, '/second');
            },
          ),
        ));
  }
}
```

### 戻る

遷移した先から戻るための処理を追加します。

【Code】

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

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Screen"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            // 追加
            Navigator.pop(context);
          },
          child: Text('Go back!'),
        ),
      ),
    );
  }
}
```

ここまででページの遷移の実装は完了しました。

## 小技

遷移する処理を書く際に

`Navigator.pushNamed(context, '/second');`

と書きますが些かハードコーディングですし、いちいちルーティングをどう定義したっけ？と見に行くのが面倒だったりします。そこで各スクリーンにルート名を定義してしまうのが便利です。

【Code】

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

class MyHomePage extends StatelessWidget {
  // 追加
  static const routeName = "/";

.
.

```

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

class SecondScreen extends StatelessWidget {
  // 追加
  static const routeName = "/second";

.
.

```

そしてルーティングを以下のように変更します。

【Code】

```
// lib/main.dart

.
.

        // 変更
        initialRoute: MyHomePage.routeName,
        routes: {
          MyHomePage.routeName: (ctx) => MyHomePage(),
          SecondScreen.routeName: (ctx) => SecondScreen(),
        });

.
.

```

遷移するためのルートをいちいち見に行かなくても済むだけでなく書き間違えも防止できるので良いかと思います。

【Code】

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

// 追加
import './second_screen.dart';

class MyHomePage extends StatelessWidget {
  static const routeName = "/";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('navigation sample'),
        ),
        body: Center(
          child: FlatButton(
            child: Text("GO TO 2nd SCREEN"),
            onPressed: () {
              // before
              // Navigator.pushNamed(context, '/second');

              // after
              Navigator.pushNamed(context, SecondScreen.routeName);
            },
          ),
        ));
  }
}
```

## 終わりに

今回はナビゲーションについて紹介しました。ロバストな書き方って調べれば調べるほど色々あって面白いですね。もしもっと良い方法があれば教えてください。喜びます。

ここまで読んでくださりありがとうございました。