272
200

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

FlutterのNavigatorで画面遷移

Last updated at Posted at 2018-04-18

Flutter の画面遷移では Navigator を使用します。
Navigator は、ウィジェットをスタックで管理します。

Navigator を使用して以下のような画面遷移を実現してみたいと思います。
※ボックスが画面、矢印が画面遷移、(赤文字)が操作イベント、[青文字]がNavigatorのメソッドです。

Flutter Navigator.png

  • スプラッシュやログイン画面の様な一方通行の画面遷移の場合は、pushReplacement を使用します。
    この場合、ログイン後はホーム画面がスタックのルートの画面になります。

    Navigator.of(context).pushReplacementNamed("/home");
    
  • ホーム画面からその他画面、またその他画面への遷移し戻ることが出来るようにする場合は、次の画面への遷移に push、画面を戻るときは pop を使用します。ダイアログもウィジェットであるため同じです。
    push された画面では pop することが可能であれば、バックオペレーションが可能になります。※アクションバーの戻るボタン

    Navigator.of(context).pushNamed("/next");
    
  • スタックされたその他画面から、すべての画面をクリアしてホーム画面へ戻りたい場合は、popUntil を使用します。 popUntil は条件に一致するまでスタックから画面をpopして行きます。第二引数の predicate にPopを止めたい条件を指定します。

    Navigator.popUntil(context, ModalRoute.withName("/home"));
    
  • ダイアログを表示する場合は、showDialogを使用します。showDialog の内部コードでは、push が呼ばれています。ダイアログを閉じる場合は、pop を使用します。

    showDialog<bool>(
      context: context,
      builder: (BuildContext context) {
        return new AlertDialog(
          content: const Text('Do you want logout?'),
          actions: <Widget>[
            new FlatButton(
              child: const Text('No'),
              onPressed: () {
                Navigator.of(context).pop(false);
              },
            ),
            new FlatButton(
              child: const Text('Yes'),
              onPressed: () {
                Navigator.of(context).pop(true);
              },
            ),
          ],
        );
      },
    );
    
  • ログアウトで、全ての画面を破棄してスプラッシュを表示する場合は、
    pushAndRemoveUntil を使用します。pushAndRemoveUntil は条件に一致するまでスタックから画面を除いて行きます。その後画面を pushします。第二引数の predicate を false にすることで全て消されることになります。

    Navigator.pushAndRemoveUntil(
      context,
      new MaterialPageRoute(
        builder: (context) => new Splash()),
        (_) => false);
    

以下は全体のコードになりますので、動かしてみて下さい。

全体コード

import 'dart:async';

import 'package:flutter/material.dart';

void main() {
  runApp(new MaterialApp(
    title: 'Navigation with Routes',
    routes: <String, WidgetBuilder>{
      '/': (_) => new Splash(),
      '/login': (_) => new Login(),
      '/home': (_) => new Home(),
      '/next': (_) => new Next(),
    },
  ));
}

// ---------
// スプラッシュ
// ---------
class Splash extends StatefulWidget {
  @override
  _SplashState createState() => new _SplashState();
}

class _SplashState extends State<Splash> {
  @override
  void initState() {
    super.initState();

    new Future.delayed(const Duration(seconds: 3))
        .then((value) => handleTimeout());
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Center(
        // TODO: スプラッシュアニメーション
        child: const CircularProgressIndicator(),
      ),
    );
  }

  void handleTimeout() {
    // ログイン画面へ
    Navigator.of(context).pushReplacementNamed("/login");
  }
}

// ---------
// ログイン画面
// ---------
class Login extends StatefulWidget {
  @override
  _LoginState createState() => new _LoginState();
}

class _LoginState extends State<Login> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: const Text("Login"),
      ),
      body: new Center(
        child: new Form(
          child: new SingleChildScrollView(
            padding: const EdgeInsets.symmetric(horizontal: 16.0),
            child: new Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[
                const SizedBox(height: 24.0),
                new TextFormField(
                  decoration: const InputDecoration(
                    border: const UnderlineInputBorder(),
                    labelText: 'User Id',
                  ),
                ),
                const SizedBox(height: 24.0),
                new TextFormField(
                  maxLength: 8,
                  decoration: new InputDecoration(
                    border: const UnderlineInputBorder(),
                    labelText: 'Password',
                  ),
                ),
                const SizedBox(height: 24.0),
                new Center(
                  child: new RaisedButton(
                    child: const Text('Login'),
                    onPressed: () {
                      // TODO: ログイン処理
                      // ホーム画面へ
                      Navigator.of(context).pushReplacementNamed("/home");
                    },
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

// ---------
// ホーム画面
// ---------
class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: const Text("Home"),
      ),
      body: new Center(
        child: new RaisedButton(
          child: const Text("Launch Next Screen"),
          onPressed: () {
            // その他の画面へ
            Navigator.of(context).pushNamed("/next");
          },
        ),
      ),
    );
  }
}

// ---------
// その他画面
// ---------
class Next extends StatefulWidget {
  @override
  _NextState createState() => new _NextState();
}

class _NextState extends State<Next> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: const Text("Next"),
      ),
      body: new Center(
        child: new Column(
          children: <Widget>[
            const SizedBox(height: 24.0),
            new RaisedButton(
              child: const Text("Launch Next Screen"),
              onPressed: () {
                // その他の画面へ
                Navigator.of(context).pushNamed("/next");
              },
            ),
            const SizedBox(height: 24.0),
            new RaisedButton(
              child: const Text("Home"),
              onPressed: () {
                // ホーム画面へ戻る 
                Navigator.popUntil(context, ModalRoute.withName("/home"));
              },
            ),
            const SizedBox(height: 24.0),
            new RaisedButton(
              child: const Text("Logout"),
              onPressed: () {
                // 確認ダイアログ表示
                showDialog<bool>(
                  context: context,
                  builder: (BuildContext context) {
                    return new AlertDialog(
                      content: const Text('Do you want logout?'),
                      actions: <Widget>[
                        new FlatButton(
                          child: const Text('No'),
                          onPressed: () {
                            // 引数をfalseでダイアログ閉じる
                            Navigator.of(context).pop(false);
                          },
                        ),
                        new FlatButton(
                          child: const Text('Yes'),
                          onPressed: () {
                            // 引数をtrueでダイアログ閉じる
                            Navigator.of(context).pop(true);
                          },
                        ),
                      ],
                    );
                  },
                ).then<void>((aBool) {
                  // ダイアログがYESで閉じられたら...
                  if (aBool) {
                    // 画面をすべて除いてスプラッシュを表示
                    Navigator.pushAndRemoveUntil(
                        context,
                        new MaterialPageRoute(
                            builder: (context) => new Splash()),
                        (_) => false);
                  }
                });
              },
            ),
          ],
        ),
      ),
    );
  }
}

参考

272
200
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
272
200

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?